From 89e476c14592887209e2d42479f617597e5aa531 Mon Sep 17 00:00:00 2001 From: Mahdi Karzari <76250959+QM4RS@users.noreply.github.com> Date: Mon, 20 Jul 2026 03:53:06 +0330 Subject: [PATCH] docs: prepare FridaBox for public release --- .github/FUNDING.yml | 3 - .github/ISSUE_TEMPLATE/bug_report.yml | 52 ++++ .github/ISSUE_TEMPLATE/config.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.yml | 34 +++ .github/PULL_REQUEST_TEMPLATE.md | 30 ++ .github/dependabot.yml | 18 ++ .github/workflows/android.yml | 87 ++++++ .github/workflows/build_and_telegram.yml | 167 ----------- ARCHITECTURE.md | 176 ++++++++---- CHANGELOG.md | 48 ++++ CODE_OF_CONDUCT.md | 25 ++ CONTRIBUTING.md | 75 +++++ README.md | 311 +++++++++++++++++++-- SECURITY.md | 45 +++ THIRD_PARTY_NOTICES.md | 16 ++ assets/usage.gif | Bin 5964639 -> 0 bytes docs/ENGINEERING_JOURNEY.md | 256 +++++++++++++++++ docs/FRIDA_CONNECTION.md | 2 +- docs/GITHUB_PUBLISHING.md | 73 +++++ 19 files changed, 1173 insertions(+), 246 deletions(-) delete mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/android.yml delete mode 100644 .github/workflows/build_and_telegram.yml create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md delete mode 100644 assets/usage.gif create mode 100644 docs/ENGINEERING_JOURNEY.md create mode 100644 docs/GITHUB_PUBLISHING.md diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index ea23ea2..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,3 +0,0 @@ -github: alex5402 -buy_me_a_coffee: alex5402 -custom: ["https://www.binance.com/en-IN/my/wallet/account/main/withdrawal/crypto/USDT", Binance__749326513 ] diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..c997646 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,52 @@ +name: Bug report +description: Report a reproducible FridaBox failure on an authorized test target. +title: "[Bug]: " +labels: [bug] +body: + - type: markdown + attributes: + value: | + Do not upload proprietary APKs, private agents, credentials, or unsanitized device logs. + Security vulnerabilities belong in a private GitHub security advisory. + - type: checkboxes + attributes: + label: Authorization and scope + options: + - label: I am authorized to test the device and application involved. + required: true + - label: I checked the documented limitations and existing issues. + required: true + - type: input + attributes: + label: FridaBox version or commit + placeholder: 4.0.0 / commit SHA + validations: + required: true + - type: input + attributes: + label: Device and Android build + placeholder: Model, Android/API version, vendor ROM + validations: + required: true + - type: dropdown + attributes: + label: Launch mode + options: [On-device, Computer, Clean, Import/build, Not mode-specific] + validations: + required: true + - type: textarea + attributes: + label: Reproduction steps + description: Provide the smallest deterministic sequence. + validations: + required: true + - type: textarea + attributes: + label: Expected and actual behavior + validations: + required: true + - type: textarea + attributes: + label: Sanitized evidence + description: Relevant logcat, stack trace, controller output, ABI, and process state. + render: text diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..62883a5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,34 @@ +name: Research or feature proposal +description: Propose a scoped capability or research direction. +title: "[Proposal]: " +labels: [enhancement] +body: + - type: checkboxes + attributes: + label: Scope + options: + - label: This proposal is for authorized research and defensive testing. + required: true + - label: I reviewed the architecture and current limitations. + required: true + - type: textarea + attributes: + label: Problem + description: What concrete limitation or workflow gap should be addressed? + validations: + required: true + - type: textarea + attributes: + label: Proposed boundary + description: Which host, virtual runtime, Gadget, controller, or UI component changes? + validations: + required: true + - type: textarea + attributes: + label: Acceptance evidence + description: Describe the tests and device proof that would demonstrate success. + validations: + required: true + - type: textarea + attributes: + label: Alternatives and risks diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..45b2389 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,30 @@ +## Summary + +Describe the problem and the narrow change that solves it. + +## Affected boundary + +- [ ] Host UI/import/policy +- [ ] Virtual package or process runtime +- [ ] Gadget lifecycle or agent configuration +- [ ] Controller or Frida scripts +- [ ] Build/release/tooling +- [ ] Documentation only + +## Validation + +- [ ] Relevant unit tests pass +- [ ] `:app:check` passes +- [ ] Debug build passes +- [ ] Release build passes +- [ ] Device validation attached when runtime behavior changed +- [ ] Original guest APK remains unmodified +- [ ] No credentials, private agents, proprietary APKs, or sensitive logs included + +## Evidence + +List exact commands, Android/device details, and sanitized output. + +## Risks and limitations + +Describe compatibility impact, process-lifecycle assumptions, and remaining gaps. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ac98147 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,18 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..b63b43e --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,87 @@ +name: Android CI + +on: + push: + branches: [main] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: android-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + verify: + name: Build, test, and verify + runs-on: ubuntu-latest + timeout-minutes: 45 + + steps: + - name: Check out source + uses: actions/checkout@v6 + + - name: Set up JDK 21 + uses: actions/setup-java@v5 + with: + distribution: temurin + java-version: '21' + cache: gradle + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + cache: pip + cache-dependency-path: tools/requirements.txt + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: '22' + cache: npm + + - name: Install Android SDK components + run: | + yes | sdkmanager --licenses >/dev/null || true + sdkmanager \ + 'platforms;android-35' \ + 'build-tools;35.0.0' \ + 'ndk;29.0.14206865' + + - name: Install controller and agent dependencies + run: | + python -m pip install --requirement tools/requirements.txt + npm ci + + - name: Verify reproducible Frida agents + run: | + python tools/build_frida_agents.py + git diff --exit-code -- scripts/dist + + - name: Build and test + run: | + chmod +x gradlew + ./gradlew \ + :app:assembleDebug \ + :app:assembleRelease \ + :Bcore:testDebugUnitTest \ + :app:testDebugUnitTest \ + :app:check \ + --stacktrace \ + --console=plain + + - name: Upload APKs and reports + if: always() + uses: actions/upload-artifact@v6 + with: + name: fridabox-${{ github.sha }} + if-no-files-found: warn + retention-days: 14 + path: | + app/build/outputs/apk/**/*.apk + app/build/reports/lint-results-*.html + app/build/test-results/**/*.xml + Bcore/build/test-results/**/*.xml diff --git a/.github/workflows/build_and_telegram.yml b/.github/workflows/build_and_telegram.yml deleted file mode 100644 index 18ed8a2..0000000 --- a/.github/workflows/build_and_telegram.yml +++ /dev/null @@ -1,167 +0,0 @@ -name: Build and Send APK to Telegram - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - build-and-send: - runs-on: ubuntu-latest - env: - TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} - TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: '21' - cache: 'gradle' - - - name: Set up Android SDK - uses: android-actions/setup-android@v3 - - - name: Accept Android SDK licenses - run: yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses || yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses || true - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - - - name: Build APKs and AARs - run: ./gradlew assembleDebug assembleRelease :Bcore:assembleDebug :Bcore:assembleRelease :black-reflection:assemble :compiler:assemble - - - name: Find Bcore AAR paths - id: aar_paths - run: | - DEBUG_AAR=$(find ./Bcore/build/outputs/aar -name "*debug.aar" | head -n 1) - RELEASE_AAR=$(find ./Bcore/build/outputs/aar -name "*release.aar" | head -n 1) - echo "debug_aar_path=$DEBUG_AAR" >> $GITHUB_OUTPUT - echo "release_aar_path=$RELEASE_AAR" >> $GITHUB_OUTPUT - - - name: Find black-reflection and compiler artifact paths - id: additional_artifacts - run: | - BLACK_REFLECTION_ART=$(find ./black-reflection/build/libs ./black-reflection/build/outputs -name "*.jar" -o -name "*.aar" 2>/dev/null | head -n 1) - COMPILER_ART=$(find ./compiler/build/libs ./compiler/build/outputs -name "*.jar" -o -name "*.aar" 2>/dev/null | head -n 1) - echo "black_reflection_path=$BLACK_REFLECTION_ART" >> $GITHUB_OUTPUT - echo "compiler_path=$COMPILER_ART" >> $GITHUB_OUTPUT - - - name: Send Bcore Debug AAR to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - AAR_PATH="${{ steps.aar_paths.outputs.debug_aar_path }}" - if [ -n "$AAR_PATH" ] && [ -f "$AAR_PATH" ]; then - RESPONSE=$(curl -s -F document=@"$AAR_PATH" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[Bcore DEBUG AAR] $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - else - echo "Debug AAR not found at $AAR_PATH" - fi - - - name: Send Bcore Release AAR to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - AAR_PATH="${{ steps.aar_paths.outputs.release_aar_path }}" - if [ -n "$AAR_PATH" ] && [ -f "$AAR_PATH" ]; then - RESPONSE=$(curl -s -F document=@"$AAR_PATH" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[Bcore RELEASE AAR] $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - else - echo "Release AAR not found at $AAR_PATH" - fi - - - name: Send black-reflection Artifact to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - ART_PATH="${{ steps.additional_artifacts.outputs.black_reflection_path }}" - if [ -n "$ART_PATH" ] && [ -f "$ART_PATH" ]; then - FILENAME=$(basename "$ART_PATH") - RESPONSE=$(curl -s -F document=@"$ART_PATH" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[black-reflection] $FILENAME - $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - else - echo "black-reflection artifact not found at $ART_PATH" - fi - - - name: Send compiler Artifact to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - ART_PATH="${{ steps.additional_artifacts.outputs.compiler_path }}" - if [ -n "$ART_PATH" ] && [ -f "$ART_PATH" ]; then - FILENAME=$(basename "$ART_PATH") - RESPONSE=$(curl -s -F document=@"$ART_PATH" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[compiler] $FILENAME - $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - else - echo "compiler artifact not found at $ART_PATH" - fi - - - name: Send ALL Debug APKs to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - for APK in ./app/build/outputs/apk/debug/*.apk; do - if [ -f "$APK" ]; then - RESPONSE=$(curl -s -F document=@"$APK" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[DEBUG] $(basename $APK) $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - fi - done - - - name: Send ALL Release APKs to Telegram and Pin - run: | - COMMIT_MSG=$(git log -1 --pretty=%s) - for APK in ./app/build/outputs/apk/release/*.apk; do - if [ -f "$APK" ]; then - RESPONSE=$(curl -s -F document=@"$APK" \ - -F chat_id="$TELEGRAM_CHAT_ID" \ - -F caption="[RELEASE] $(basename $APK) $COMMIT_MSG" \ - https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendDocument) - MESSAGE_ID=$(echo $RESPONSE | jq '.result.message_id') - if [ "$MESSAGE_ID" != "null" ] && [ -n "$MESSAGE_ID" ]; then - curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/pinChatMessage" \ - -d chat_id="$TELEGRAM_CHAT_ID" \ - -d message_id="$MESSAGE_ID" - fi - fi - done - diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 609c379..9a63ad1 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,57 +1,133 @@ -# Architecture +# FridaBox architecture -FridaBox extends the existing BlackBox virtual package, process, Binder, file, -identity, signature, and lifecycle implementation. It does not introduce a -second plugin framework and never creates a replacement `DexClassLoader` for a -guest. +FridaBox has two deliberate layers: + +1. an independent host product under `com.qm4rs.fridabox`, responsible for APK + import, integrity, launch policy, agent storage, runtime presentation, and + the user experience; +2. a retained virtual Android runtime responsible for package, process, Binder, + filesystem, identity, signature, component, and lifecycle virtualization. + +The runtime descends from the NewBlackbox/BlackBox lineage. Its internal package +names are compatibility API and are not the FridaBox product namespace. They are +kept stable because manifests, AIDL, reflection, native code, persisted paths, +and virtual process dispatch all depend on that contract. + +## End-to-end lifecycle ```text -Host launcher process - | - | import original APK - v -BlackBox virtual package manager - | - | allocate :pN virtual process - v -BActivityThread.handleBindApplication() - | - | create guest Context / LoadedApk / ClassLoader - | initialize virtual runtime and IO redirection - | populate GuestRuntimeRegistry - | load Frida Gadget and wait - v -Frida controller attaches - | - | select guest ClassLoader - | install Java/native hooks - v -Guest makeApplication() - | - v -Guest Application.onCreate() - | - v -Guest Activity +FridaBoxActivity + │ + ├── SAF document stream + ├── private immutable APK copy + ├── SHA-256 + ZIP ABI inspection + └── virtual installation for user 0 + │ + ▼ + virtual package manager + │ + ▼ + allocate :pN process and dispatch ProxyActivity + │ + ▼ + BActivityThread.handleBindApplication() + │ + ├── establish virtual identity and runtime + ├── initialize native IO redirection + ├── create guest Context / LoadedApk / PathClassLoader + ├── populate GuestRuntimeRegistry + └── apply per-package InstrumentationPreferences + │ + ├── disabled: continue + ├── listen: load loopback Gadget and wait for attach + └── script: load private Gadget + agent.js + │ + ▼ + guest makeApplication() + │ + ▼ + guest Application.onCreate() + │ + ▼ + guest Activity ``` -## Runtime boundary - -`GuestRuntimeRegistry` is volatile, process-local state. Each normal BlackBox -virtual process populates it after `VirtualRuntime.setupRuntime`, `NativeCore.init`, -and IO redirection, using the `LoadedApk` ClassLoader. `FridaGadgetLoader` then -performs one synchronized `System.loadLibrary("frida-gadget")` attempt in that -Linux process. The static Gadget configuration pauses that call until a -controller connects. Only afterward does BlackBox call `makeApplication`. - -The host status screen uses a small SharedPreferences snapshot because the host -launcher cannot directly read another process's static registry. The Frida -controller reads the authoritative process-local registry through Java. - ## Import boundary -The host copies a document-provider stream once into `files/imported-apks`, -computes SHA-256 while copying, inspects the ZIP ABI entries, parses package -metadata, verifies the stored hash, marks the file read-only, and passes the path -only to BlackBox's virtual package manager. No real package installer Intent or -PackageInstaller session is used. +The host copies a document-provider stream once into `files/imported-apks` and +computes SHA-256 while copying. It inspects native ZIP entries, parses package +metadata, verifies the stored hash, marks the private file read-only, and passes +that path only to the virtual package manager. + +There is no package-installer Intent, PackageInstaller session, APK mutation, +repack, or resign. The original guest package is therefore absent from Android's +real PackageManager. + +## Instrumentation boundary + +`GuestRuntimeRegistry` is authoritative, volatile, process-local state. It is +populated only after `VirtualRuntime.setupRuntime`, `NativeCore.init`, IO +redirection, and guest `LoadedApk` creation have succeeded. This gives Frida a +real guest ClassLoader while still preceding `makeApplication()`. + +`FridaGadgetLoader` makes one synchronized Gadget-load attempt per Linux process. +The loader reads per-package policy from private host storage: + +- **disabled** emits an explicit clean-launch state and never loads Gadget; +- **listen** loads the pinned loopback configuration with `on_load=wait`; +- **script** creates a private per-guest Gadget copy and adjacent Script + interaction config referencing a read-only `agent.js`. + +The build task `verifyInstrumentationOrdering` statically verifies that the +loader remains before guest `makeApplication()`. + +## Controller boundary + +Gadget always listens on device loopback. ADB forwarding is a controller concern, +not a guest concern. `tools/attach_guest.py` forwards a bounded port range, +probes candidates using a compiled registry agent, rejects unrelated endpoints, +and maps each endpoint to package/process metadata from `GuestRuntimeRegistry`. + +Before loading a user agent, `guest-bootstrap.js` assigns the registry's guest +ClassLoader to `Java.classFactory.loader` and reports native module enumeration. +This prevents host-first class resolution from producing false negatives. + +## Cross-process state + +The registry itself is never treated as cross-process storage. A minimal private +SharedPreferences snapshot lets the host UI display the latest package, process, +virtual user, process slot, source APK, ClassLoader, state, and error. Reads use +multi-process reload semantics because host and guest processes maintain +independent in-memory preference caches. + +## Process recycling + +Instrumentation policy is process-scoped in effect even though it is persisted +per package. Changing mode stops the guest before relaunch. This is essential: +a native library cannot be unloaded safely from a living Android process, so a +Clean launch must receive a fresh virtual process rather than attempting to undo +Gadget initialization. + +## Trust and security boundaries + +- The imported APK and selected JavaScript agent are untrusted inputs. +- Imported files are size-bounded, hashed, privately stored, and made read-only. +- On-device agents require an explicit trust confirmation and execute with the + guest process's access. +- Gadget binds only to `127.0.0.1`; remote reachability requires an explicit ADB + forwarding action. +- Release builds never silently use the debug signing key. +- FridaBox does not claim to defeat kernel, SELinux, Frida, virtualization, or + hardware-attestation detection. + +## Source layout + +| Module | Responsibility | +| --- | --- | +| `app` | FridaBox application, UI, import pipeline, modes and management. | +| `Bcore` | Virtual Android runtime, Gadget load point, registry and native integration. | +| `black-reflection` | Generated/reflected access to hidden Android framework APIs. | +| `compiler` | Annotation processing used by the reflection layer. | +| `sample-guest` | Deterministic early-start and hook validation target. | +| `scripts` | Source and reproducibly bundled Frida 17 agents. | +| `tools` | Fetch, build, forward, discover and attach utilities. | diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1fda31 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +All notable FridaBox changes are documented here. The project follows semantic +versioning for its public releases. + +## [4.0.0] - 2026-07-20 + +### Added + +- Independent FridaBox application, Android namespace, brand, icon, responsive + workspace, runtime dashboard, settings, and per-guest management. +- Per-app **On-device**, **Computer**, and **Clean** launch modes. +- Trusted JavaScript selection through SAF with private byte-identical storage, + SHA-256 recording, size bounds, and read-only execution permissions. +- Autonomous Gadget Script interaction requiring no ADB or computer after agent + selection. +- Loopback Gadget endpoint discovery and guest mapping through + `GuestRuntimeRegistry`. +- Guest ClassLoader bootstrap and native module enumeration. +- ARM64 APK inspection and clear rejection of incompatible/split inputs. +- Generated sample guest and deterministic `Target.add(2, 3) -> 1337` hook. +- Reproducible Frida 17 agent bundles with pinned npm dependencies. +- Explicit production signing configuration, R8, and resource shrinking. +- Android 16 device-validation transcript and engineering documentation. + +### Changed + +- Gadget now loads after virtual runtime/IO/ClassLoader setup and before guest + `makeApplication()`. +- Launch-mode changes recycle the virtual process to prevent instrumentation + state from leaking into Clean launches. +- Runtime snapshots reload safely across host/guest process boundaries. +- Private Gadget and agent permissions comply with modern Android executable + file constraints. +- Guest-visible inherited product strings were replaced with FridaBox identity. + +### Removed + +- The inherited launcher and unrelated GMS, Xposed, fake-location, floating + overlay, cloning-list, and legacy settings surfaces. +- Obsolete launcher resources, translations, UI AARs, and product documents. +- Debug-key fallback for release artifacts. + +## Foundation + +FridaBox began from NewBlackbox commit +`89b59836c66f173756a4ae258cf379a957649820`. Upstream provenance and bundled +Frida licensing are documented in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d496f49 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,25 @@ +# Community code of conduct + +FridaBox is a technical research project. Participation must remain respectful, +evidence-driven, and safe. + +## Expected behavior + +- Treat contributors, maintainers, upstream projects, and users with respect. +- Critique technical claims with reproducible evidence rather than personal + attacks. +- Respect authorization boundaries, privacy, licenses, and coordinated security + disclosure. +- Make room for different experience levels while maintaining engineering rigor. +- Clearly distinguish measured results from assumptions or speculation. + +## Unacceptable behavior + +Harassment, discrimination, threats, doxxing, publication of secrets or private +data, knowingly false claims, and requests for unauthorized exploitation are not +welcome. Maintainers may edit, hide, reject, or remove contributions that violate +these expectations and may restrict participation when necessary. + +Report conduct concerns privately through the repository owner's available +GitHub contact or security-advisory channel. Good-faith reports will be handled +with discretion. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2ed4b2d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing to FridaBox + +FridaBox welcomes focused contributions that improve correctness, +compatibility, observability, documentation, or responsible research workflows. + +## Before opening an issue + +- Confirm the target and device are authorized for your testing. +- Read [docs/LIMITATIONS.md](docs/LIMITATIONS.md) and + [docs/DETECTION_SURFACES.md](docs/DETECTION_SURFACES.md). +- Search existing issues for the Android version, vendor ROM, package type, and + failure signature. +- Remove credentials, personal data, proprietary APKs, private agents, tokens, + and device identifiers from logs. + +Security vulnerabilities should follow [SECURITY.md](SECURITY.md), not a public +issue. + +## Development setup + +Use JDK 21, Android SDK 35, NDK 29.0.14206865, Python 3.10+, and Node.js 20+. +Follow [docs/BUILDING.md](docs/BUILDING.md) for Gadget verification and the +Windows no-space native-build workaround. + +```text +npm ci +python tools/build_frida_agents.py +./gradlew :app:assembleDebug :Bcore:testDebugUnitTest :app:testDebugUnitTest :app:check +``` + +## Pull requests + +Keep changes narrow and explain the runtime boundary they affect. A useful pull +request includes: + +- the problem and why the current behavior is incorrect; +- the smallest viable implementation; +- affected Android versions, ABIs, and process types; +- host-side test results; +- device evidence for lifecycle, native, Binder, or virtualization changes; +- explicit confirmation that imported APK bytes were not modified; +- documentation updates for new behavior or limitations. + +Do not combine unrelated reformatting, package renames, or generated-file churn +with a runtime fix. + +## Runtime changes + +Changes around `BActivityThread`, `NativeCore`, IO redirection, Binder proxies, +Gadget loading, process allocation, or package persistence require device +validation. At minimum, test: + +1. import of an original ARM64 base APK; +2. absence of the guest from the real PackageManager; +3. Computer-mode pause and attach; +4. Java hook execution with the guest ClassLoader; +5. native module enumeration; +6. process recycling into Clean mode; +7. a second instrumented launch after Clean mode. + +Add exact commands and relevant log lines to a dedicated validation note. Never +commit proprietary APKs or third-party scripts without redistribution rights. + +## Style + +- Preserve existing source encoding and line endings where practical. +- Keep user-facing language precise; do not promise stealth or universal app + compatibility. +- Prefer explicit failure states over silent fallbacks. +- Keep network listeners loopback-only by default. +- Pin and verify externally fetched runtime artifacts. +- Add tests for parsers, integrity rules, state transitions, and regressions. + +By contributing, you agree that your contribution is licensed under the +repository's Apache License 2.0. diff --git a/README.md b/README.md index aabfbbf..e198aad 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,295 @@ +
?*PD#aWk&2&^YS<#xI3=~TMQV9U>Sc@6>y$Lw7HRY;X--?DIZsJz*&?lVO4{2N zY420gIkrgWoRaRfMY{Kt^qwu!d#9xTZIS*zB?Gp_27JnfVv7ysl#SFD8|f(_+%k@|Wob 1F?yvDc|(6fMZ;TgEnRS!R+#*0Qv}Yt^#P&ClMa_WtOy94Wcn zYibYgs^ziG&3mVI;OnydU(y9^>Fasb3*XHwluMtdy1eL-M6ucOUR(8&XVXjkp4Wyh zFTEyKmi9a+SG|1S)bhIY=+@;G^F%7ArF+g*ubMQuYTa^+t;?&^gldjGS3Rp %T2$|Eu0mG@*g-#ZQqHjnnv>)L#71(P&=Q*KGIVrOS$zuobOw8f|GS z+VV8o%T~14X>_!$=;+hvoVKEKo<`TQ6 vGjoxc3dhcoUJzLTD zPNV }@M&@6()fY~`GDnscwMoO@4m-m{hS z-f7PNwsQVI%>`_$7Vv2;6kD}WPHU0cszrLQ%&k-wbG0tE(+Y4|wd5De(l9Om7_DWm zT9)N$`IW3%ev4&Ao0e~n*2+^YE9YtXELpW`7t88xTHbrK)~sq-b56_s%Br +-=Q?KAn@()|{O8W|fc1{#80h?Vg<4m2 ?Czdr#-wvo+`5 z>74(z=KMdM3v6#sDs=e^ wwC4L5oIKe@X7&Fib5w6A*U zUN2jFy-xQ=+u9p_x;Lk-y*W?!*0Qy?*6H5fwl@6U#hyvw2`6;@FTC|D@bJ6wwt3Rg zJ4GH>ueIFd7Z+%YC6E_g?A7mz=nN$>qVfBM)x5JUo=!^hq~CW8DL(b&rGI`MA7$ zwCn%!XTMsXrmcH&@9pzrEYEl8zFM~K)jGY`r&P{9d;R)R_M3aJ-`vW6d+qhxZR_5> z(|iAI-TQxfAK2D^;M4yow*KR`)a0NSbJX&m%e`0CT0hSzf1a2AyrB1Uqt?$&%AcF1 zKeuSTN8dY AN7zy_&6hf665szf3y6VxF0dC&S%zs!SXnXr9Ge3JcI4_Bi7)@ z% A}VH+>LWwZZh#2C-WkX1pqx@yTGu zuMg9iHcsa%oG!HSt>6EgWlA6YVzh-H8x+=Qi)e3L;Z^8+V9n<^L$UOYV)=&R 0C*Qm@v~J^o2I`L$m03a$U?C)_?R z*;N$wZxfIIM|J+s2G5P$q&Dy7Fy1L-9A;I#+hOxkug{@IpLg|aGK)?zU(Rj*y(qNK zcx&8d-N&q!{;5{Xt1Z^AvRMDx%Kd+;4g03PbHCU2Lq=)UIdQt$%K}|FiAvYzKZe zdw!EuQY9V#K3f)RJMkAeYrl3**YBO`YcK5fIb!LhNxrW9$6Ui-xh21JkI(doe(4eZ za?`Q(?&c+rU%mEvp6&hkwfFsOpWCl}u4nsRe(ihvi{WBJ|L SD)6np(q4B0+^L_oIbAKHw3E%z2FMVr7ziH(3tIh zqn2-tT5lS?eQWf7)0pF1W6qn#Uf&vf-!$%d>7hkieCthpe{c0^-x|knmMFe0QQj;` zeOr>gS+e=IWP7s|_iZWuW~t%ZQsd3i?iaY^|9A1ctYK^aDn0sj+Ur*t>Dw~rn`K?j z&zziR_ii?;L~ZTS^;ecK)M%PW#swX47ESe@NbY|*uR$DC zaaYB}LqA?>aK+0n?%AI>`^pZ(dpjn4s+jO=M;H6f3D!Cj{{PRI`1{4g?-`RmznJts zV=}+x6mC|AeR7Kw;!3!+jwznCIlw%9ce4^N$MoXLY5pZE9-Xt^(QN%=;;i3-vljn6 zch%or{nc!Bp}D)8t+p`BKixTR`_Ac?cg}i$dg}a_2ObFN@lG;%!=W8 ^;D;YX;ZZh9!qBBb;?3niSR?6#pZ_`{RJXMuydw8Mv41 zT6LL0Afr*5qe=D3A(IKt);{;oeVjC(k7KSho1x4hQHDmr3l3Zxc5TwP-n`j?%cgqM zek%c$gWQ75mUEhwz6N? <*J94mIcnbE=-a~esQgF*u4DrCVOixo86n%|8EfZ z$slRLr0e56Pq<^>lsR**^*8v$o3A+}p>mL4=l4$Y-&_vXhtz8hZQjMTz?%Edfuape z<{Ox|@pJq?qpA1ym+=%Q$rZnLu68)Q`M^$Xo0HRRwn*FT`F}vF=8*A=iSj>x*_G{< zJSDVYa UsL)N=b%>R9Gw#|jz)myr4xVBVtU1$)janzW=Z23iSLA|-gv^*&_ zp?UL}b+0t8`uykQ{@v%<_g-J_aQ*$BEyo#kB_ x0$7McAdAgl83}<>pi=c|Gs{H z@8;dsyef?fI?m=9*0-jwTs4>3Qovb~ i@pE>>&Ao$>PJ`jMA6WFE<^~uKr+ef5CV+*PUA3 znBA8rS5EaiFF*5;=nsZx&h|e}|9kU!y6Fe2Z^0`Ywj3^0W#pZ3;Cs9MAN@U><{!Vb zJ^F9L%5RS=wRY_NeB9n)iGam~1jlA}?o9zFl306HmYtiJ{r%ls+v*<%X;*ekOm^>= zcWe`}oRs1@Sv_!B$j(Wr-qZCHk4>?hoaQ^*yzrTg<@#8wqOFrIM63`<3|#C!QB8K& zqC}ynC+9X+o4;S=w>o@d+SOfC|7VA |!=E3K9>uubnp4>MSqsF6NoXrrO-hc(SV`bBZGSRQ;s$^Bvp8 zY-g2Z9!^`yDRgR@Y3lX$oSPO1KEJuM _nT(x{2XP{5uQPIdbo{mL_g3le6e4NC{s+RTt<+9ngS8P6hM68h~=!9bEk_@?` z2NAu_QZr4w6s4|hVAGN-{ru5IY2%ro)4J8#+DgH739C;Uc4v8S3ox^0-`?f5XVPi& zhdsf5>tD@$A#*HBIr#hyF{#IrSvj*`?7dSr=kf(-L0=Qq3N@wnX-|&a3BI(j&p_4J zad}qoMPJLeA{W2kvRppfC~WeTD~)Z78hNrxa@gmEKU{g~a%AROQ;+IfoKvUDU#og^ zB|g}pku7kMwYzLY>ekSksWaF5A1qtexi}=Daq8Nh^hKwy%9dB@Y`K|#kazo*O_wa& zXK1%A`WF7BAm#nG+})+YcPd}5{jNVxX_L!I)vL0_ic(rLmr395`M=~`bhV&gMB>~E zC3cyh%WuoiFFYn7p|x_w?B@v^KE1s0c)iWJ^oJ8Y#n$RP?wC0(o ?sS%W+QHps?_OoiD}Q(E-R`xEG_J(YaOhtsa>~!j zR(D+jQ*N-R*P>~eHzr%fZG5#ZI&baAb$5zpXng9Ns(gvp#^H<4&(;~CeDgzCm7ONK zopPu?GBbIN>T|6pr!&{bs&*+&I`Ubv-KcfO#q(v72U=w&wuN#RpWX55q~WaCvbnkc z`{RD~`Mh~4llSJv+^4NI>~=pcwTGYJ&D@dbfA-14?GgG|y+fd--tszF$TAFE+Ks zSO1^;<$Ws4g>~BV0{3YyJfHUI)U40jKJELV`=RJt!j#r|3-`X&TvGk}@%^3vh1sbF zd|J;7+M9UN4xCghsJ)Q oa 8(Pd{vqmhSlpbH6MJ^+{2xnYdUwbK>HLYloy3 zUT9#u+kG@3K)^dPi-T1q;Zp3p#63w%n|o3>UQ%zA^qJFgsYm-wTg9gtCTeS%X0C~7 zHJ*9W=c3?a@$)H1O*eL)Rbk<7-sI_|H{Z;`=Wo!ArzYw?E0TQ #t`F4n^(m0 zK69v?w0dD+^2B=K7@^(jS65n1ntSHs!Nw(m=QNit?n(dhDwfyt%!X~vGItiR$sD>8 zu*L0bb*UsXXF$U|)lG?uuOyrozAxyyZc dC2n9 kJ%F^Kgo_Yo)Vfcz0i2GK)HPS|5+c7rR<3?e&5KSxhUz`E1CNbjv2Qo zMO _(y!7ZbmGLBx34ywW;j>m z&D`=E5mKcAEBE(FIJZoA!2CPoz|2;L*q1@mjr^0C4b4`aertAfUHG|4Q{V2HI{R#M zAkVvF=1UwoFBS=|I%2^3gCpp4^_-t)+!Tx&KS)pKkyAQZB$nQ_a@uq2`tZvUf~xb2 zL-pEy4~w`jcy; wT-6i8?mIJo-B!-B;V&!G)}MOH%hs0ZzfCEgf7_8+eTUDbw>LXV z4oh9?IFi+qW7fJPsDfqY_PuZaGV&%=^ZcIX 3zAt9sGPM&CCC&9=Z4W z&|1UZDTXSK9?st2cw$dKe@4yo(>spae%shzzM;%$Q)lb`$Ge_1T%Y;WSN@Wnh2)p+ z3Fa!7Uw-ELt<{_EkP~#!xnq%i0h7&xhb&D$T(+8iXcSt*s;BtxP=|Y7W`Xt1|1V4H zt*%%rmDU8vs)YHkU$N7WH&Z~c;Jx0i>rZ2!31>LU9I;^JJ>uBBVY))IsX9khh5~1O ze9_7OzWR4(A1!ZPduM^;{r2=P=LGk5-&6kj;?1@SO|2}C#bRyE-&kf>3%JTBO0i#R z;<7XBF!gWK^z>l8)O)@1b`JN`h2{k(J>ml{FP3}1>bUjmmEWt%Bi2h_nQ_c`N$Ky+ z&*YvvsIkf09cT|)!@zUi!ZEvh-KzKwnK@S!o*tOCzxIML_wR3A|16jD8wTYohrR#l zqO|bRX(x$yzk?pu9a$frl_{^I_$JIjrl)~zM`QS$EJc$i!Vmg#Pq@3AF)wI(^`B?y z|5ry@{-4%l;7?{SUBuwa#3<<#l9adjhJ&Yy+jqYQ;`iROcqj;UG>XMI?lLP-Ji#D7 z;eFeN7t4S6Y`4e_i_>A9KVe101%ns^ 3JSu}E^ zX!q(EF})9bhW!6sUNaVdczkS`?vwZBk3MYFd-l17OT?E$)nj6$&qsZq_g|*DO0qUf zu75A#_-Mt1tmtdGd-uJUEO4}W_DJe Ve=wqcph}g%7WJzn*qb@R7!)ABOwe4zcqw^1bYT!d5grpvUpe1doo5 z%3?)oQbiVNK{|3pvdw+J7QK^AzTj@PTK!6+fOx>`x +yx>;2 zOjE(TN7!HQz>$wy#!mW)N!tJG59{w;q@DXz`{Kr5923;bmFB*2(b(Is%$h5If797{ zPXt!HWG?o6$FwwIg;HSLB;_BUR2epVM+GtXpEt;EHIThzvQfaWdee?99VyoQ#nT%3 z^Yo1Ns~>r=K Tff4{+5GWK0sYC%emoM5!hu%7B8llb*-VzlQ%#}+O#O}3J&V;fg;W<9 z@BNZw#r*!aOP^R@LcE9}`~G&e8iuwVg^Trq8;(tOS#{W^Ram7%So_9g6EzX-DVKw1 zTvmHnEE;Jn;F~4z_44l@#gY+@HX43B{}1|P$~@wI-1+ZN@XMU$-Em8U&ux~tyxHPz zv7OczEj<&pq7Z8%6P-E1wkJP1uRX3lMazu8P}1ojM~|9=c%W KR+)*(p%C>T{#W-DIg%SiGZH1=ILZb;lHJiL5uqrS^-xmJ$?mnX$Ic?> z*&9_JELK|Lq< ^MHNI5sn866YiM^%{Y)z*TBirC>!?C?#mbLwh+~l#Y$a=Z%C|Tn?6Ni zR>+o3A9Zgo+M4lzLx)jZ;i~IYpO<>erWY*^Ze8N`|NAF5RgtKzkAn-9z9u!Yc{uDc zanTKXz|pbL_pXV`y+p}<4vK6~Sk(MD>JCO#CWkRk_P5@mF598rm3;SIf4J+p3zHrw zuiY4RBgr+JQM@kkpBX>PX?5>Zlduns?T=idX17Nj6p7o&9G&m jIS*5D43bYEkWnW#C+Ed_J`c3>3L&E?1FtZE>zNCW*4^O&! z<^@gdpDtk%X*AVi@029V&@A0;2Hzh}J$1}2gF#})H~#q#*>8l}K4VnA=O}fmK`=5Q z=DL{mt!di#u7xG524|JUr|0Yb78RFM3|wfEEAohguTiXHn`^Jxbr%D-rIS?6k7k;O zsxP}{FzLG*3!{qgBjHC6OITkjh3K+6DTc9T%4apQ-7pn8HPt%E+%oZ7=EH01@k?c! zg08FxcCp+PU%yanmxASiP`#2hsrS0XxW0*3g-3ewRmi5To0XGkyghUG73Ds2^=Z=$ zgl4FHW8|N+T|rB+YQMR1* qo29l8g_;R%ZgVjU$+vI(Eh%V@6I71 zJ8_i-=Y*7! Ih~A$}oV#fSq!Zwlm{9_D8} zlyzBVGQF`n(m}v0c*6bS?%Nx?W0mgSV*GYxyFh-1?!_Oq_K7?V4bjt;9BkfcWIkX& z)*$pDyx^^b<3Y~;&p#&LRqQfxyJPTEf6g=&gV+5Fw?!$2Tj)IO?rCC+NK`rZrS5;& z0-gj@<$DV&Igg0EQLfpVR2t5-rr@>c_tGhXQ(C`H7t=YYG4+YzyA^Lgar7JbYwTJe z^diK(dV5CY=Qf`;&EJ#?Id7^pRjRb@Y)BNEP`GIR|3eELg=OqEeK1?1Ytq7bE>T5i z5w}DW+n)r UXwP2p=6xT>kSHJY (NjL6 zaw1>p P1`$W)@Xm5(b_B~G4X6s zp`^)9<#`V|gtS@v9&oKwaF%$)-l4c;t@G^7O|v5(U7p}2`$A0j_p7#ZGv;(AW@H}} zk$K21#pLjR{~4p4U8yZhJTePKG8pUce^1DsY3}XAV*E0)LULiY)5`YAD_L(DbN*73 ziPq7(t-8wc& =dtnbq$BSB7Fr^t7$qm$$ml&=K7% zd#lQrYhvy3%B7DrcfGtJqU9)X=fJl=)20TsI*87hf675XLs8^~NIiF0!BT65!^WyV zqbpwKXsvDDEj|6!tY7>7cWcS*QWWHP&b8%$(22% Q sE7K*0mkk?PYiOp;EwA)d^K5XKp#U&yvx;q*OOa=~k5T+mCHI zlKD?ho+z}qFtO%B=ECJ4!%u75wCmnEz5kC_`z+}LiNfw5ViT77&e)c6!RGA!8mC8h zoc6Dd6Ww&qS1W$u#DMFk_orSy&up{nYj}OB@rA^{I~U#-YM*vhmFwNI=oKG#N-OPc zzR@^+o9iYgt~#T;?>5a`!>N4v>cl;#w(|Y=l{#NvYjt^3O+odX>a8cVCtv+~{m|CC z*A_=F>ae*USd=JfChJ$>yXMIy-Q_oW=h|8Qt&B^$$-ChuyJ~p-o-3s(_N@I+awK<0 z{JQ^gjjq+=c6v!Sve7Rt6q0PzQhO2vvZtT7L^zmKcj^oZw_c$jg?`E8CF~R zWZU<-Qw|#6iQMtPSMCnK-AT81u4@k-$??`-Bz*U5OqEjHqwUR)yfy9}jNa;<72wBq zJ~`jY{$5`4#QSUKK4FYWpL*}XUYn=Oe;Wk`ompGWVl8MM^v1TOuqx}`vzuZ!*`05e z%T=4&h0XhG%@TU6{Oh*XUH3nVz5M<4?cBOu+PV+Lr|ti`Q{n&a?0{t%uXp~lD!5mx zbFcGo+~lVppMN&BH#NHM5ck?;-rJY@cRrRqxf!>jbnkvG({>yF?M+!#-|as9zUTN) zPW!;Wtw;AMoBfmBrmB4W$G>Z >GpY&y>tz50$rl9}#!siFX#@~OO^)`9_x5qrMwB;-Q=X^hIA#0} z`rq-tP*Oe1R&-V2=n)8Wf=0DoA^s~ag>Qj1m4;4RIG3j;N)mdRp zmv{M1W;4sVu|95jp?1cG* BN(?=7nO{p&Bc#fAq5)hgvw z-%Qd!v$*S{tXieuy-V!@H|I{?WuUk-KYU}^<7HE&Gn!bo&f+`U`!=oMNrb }ufyR&$EewP38 zuWO=8Z=Zg7Z{y-W>*mHR_3YhU?=NKe=jWH#x35S3vAg)xaB6W)Opxs)L(`c#?@f;M zrA`Y>otpNrar*zcX*~PbqH|sxEuHQ9E7xL9jZ7U!QpeY)%jdVN|2!_^C8p}CP_kKa zMnWlf)YXI*v#_UPr7t&3o)`RHpksEM65mPbdKbCQlu1n!E9Q5$zWqMmyX2~KLgOXZ zt;r#IfhSU@TY6njj(KIcH7RI^W@nn8-?Ecwv-A3nEUC?&)UwEpVU~$%x6e+EyUvEM zI5HMAEn^m+A;36u`OJL1Su-Z~*m1p_U3kr0eNMnKj>}U*&wcK*)jwpKv4XEQDq(7> zTh}Yksmo4ft$y$8b$^xO)tMLPYO1ahSw8O;bNKpw|Ds-4U&!4RvS^*$?TKqn=$+O{ z*l=$78}}_%X_u!koeND}ev6Cy|C;s8LeYV?Z%uFKY c52Vx_lqxj>CA_Hv)|4Bv{ky`AlvjU@2Xjr-;mg2b~QA-`@T@O{t+iRo|gyu zlFk$!*HC?P?@(Es)u&jo>7Lh*%$`>>@pw 3a z+4H|OI&ti-kFFgr_ci0wF7{lXF9#Lpl(ai8|MKoi)M=5g*WxBeZ!Pdh|66)ftu(jn z*2*^7J2#xH*KE6$aol(N64U7wUytWsKfC>c!_&Lv4-_}?&bjSn&9(hO@cFeBkL88_ z&3@=@ooDuVO1z)^lYO3j7SH0N-&MXil(|p(d0^MR)*n|r7N4u~Tk<;1YT4TVJip#- zv#u_m++bh% %sE#``m8{AFI9ecKYgFKR@4pFY$BZEq$SzKa_Xx`@ivh zZF6V*BbFU27?ccZSVVdlBgF*}Cv$f*&0!H{kY!-t_|L$|5mU&-${?oAq7}0v;HbEW zM%ab~kB{OF!f%eviCD;_=-Q_Kt?+k|Q|pm_1|LqIpUJnxMGb80Ts|hoH%(LV^_%1Q zD6VO?a$=d!2?5vhY#yyWI+dF=8frWyN^wd}Ug6Nppp &UNzdG}Mlzk7T8yPxt=e)SIzEEQi&j^LH|DhY}B z+#*(Z#P5QDJ98nYh!O8SYa`#&M!H&dyjF|}u3h~{e$+e&&SIUBu5PyP9(Tfg)g#(R zBo`ih+ukK|;`_WGQ-8B`dK}5vsnuDt-9gkLLFzBVv(xM|u9h#?NLXmh+9h<>x=mcd z&ZbNFwfMIA@pBdb{uFt?{rvj ex`KJ$bpq(s>K4E@bQP3GMc| jM~F6uN}v85(|GkjywV+dOy;w}#>b<6C`=i}`st z5aIKH=|2l210w?i2MYru3kyvo9GK!*8X20PaUkQdVZp&>4q+{pg!K;(wF@YNj@C|c zs(x>9Ycu$0ZL#|st;^rtwVt36yy{8EM45)XCqC=ucy4~?&~y3Q#K&%ZcRgo2G;d1z zo^!uH?{d{YkyBe<9xXooP1WMm<>i-GM@TwNwF-?1S{-?qY3p<me&5Wo60c=i~NP zB|UCBqF(U&=4Q7eo{!7c&2^Y8&3b97cg4cJm7i}-?3P+s_*kWC^0X 5U>Lt|%KweRcSQ7VzPz 0dH?AapSS(}=5wFIuU7Fm$>b@4Cmr6n%(!U%d5fBa-R}e9 z@~$d@pSx9SY8dA={fuqwudjGe*rc%Zk3+M7PX;4fr;5>xcGayX#1t9crl?3Tux9Oe z==9-_3!iS?8b`N^*b>LZVt-aHIBIcvjsvrKf6MrlF&_`_R>kfJO^aMmX@#t wD`v?z9o)#o)|cBUTKc&bo6@Wh0Dep?TB z?eti^=gYM-eGiXmG5x%9t~&Ch%jx}jS!ciAnrAlCK>O~T&2mW(tfopa{C9e#cDrW# z|F>q+n?Bx%jP`wa`KINOBOhHa)tbLkmn=BK<4{#%og!@aZOddn)AokrJ0364zQKCp zuEg|vH>TY-xn$DW^Re>925pUwl=nTc<-RLhAN_j0@o?O+LqE8#@_C-$u(A66iE_>z z-c#63XUlc#uPghs_w>gz=1VV?% EVB#!I%bF*I;zyZ;sLj# zItRyG7CGs_MA!ch795r}%j2DNTTmc3L-20ng^rECTTI@mPWV>PB2(G$= Vg`@ss+psEEG8 (ssgO0fQp-y? zc+zhrvHNC5eVD_r;7QJ< q#@O!#lb9M(!slYhI2oPF|ahwXWg{PQ|#>wo9gQaLWDLOzr85n@t3I~zlEdh zzjLOEzE;4rLyih}ZajD?7j+`{(iiDF8=ODch}&{?otS?52Wv$P`--?&o|5bi52OB5 zycP=ld|MB+MjW@g7GF^$c}eB9e%43sjsKcD9>qk32}=onWw^T9;;)m;xl`9Os*GmU zxvoxN-`(SWEN|VumE2Bj$>EI;N*k+K*QETv6@Kd9rA=*r4@t W2CJ(eXeW=ZY+up?;a>8H_`>vmq;6(O{Pb!B{^ z|L)l3ZmTALi;*mz{%v)tN3ztl6;Fa%By1W37V${Go?X53cifgH@r2KzQA%&JXP5Zp zDg4nrp NwiI z*3iyLs%3V1i%3eS%u1f=#p(LTW=OI-JvLNj3AEpM%;bSX;<}F!RS!=V)Wp_A@h~Mw zGVDkT$Xzqx3~r{^C|%jrU(Cp!(-=9IjQ61aW+ulO2LPT zyh^=dQ3Yq7iA>Zed3oT1R!VcWLAuJr18hoK1-x4nwsa^v@iKf+JaC0uU8jDPx2^Y+ zSyK&iWv+{HHpNVy($?0o!hNra@v2IN8{S`?e_Tij(>`%V;_(;bhF6(gGZtOjmNvVc z^-<)6KT5R+`C=uFYy`5!Rw=LcDtqy#Kp^`>kMhQdty|7tZPgYzVsf3OQ$Ao%Zr<~U z;> I_$yqR>#>7TAh+fH8UE*n9H#peL%Kt%6i#fjICf3pu(t4Qg^T|;c1&VV;`HhM z6E(@<`|m5Y$3iZJDCs$_a@a7Zz4r5N?m22F)_u&5y-@b#UiiOOrT98svm;F6X}LQb zobF847P|9fT7m54y-$`I+AI>|(LeFS>-f{oDd!HJULmqUaNlmZv>1&$JJind? j2{Hotk>d1oUVuI?}TFM3mNmjtT5%y`VUyzqDY zj6)Uc;^*H!^?@_$f;7XD^G3dIN&*Eni_Nx+cRXJzyJBb4T!|wGHqFX4E}ZqmX8yxt zpZ@<&x~TSQ^LhS7CoVeN`~K?puV=3`GanzQJ^Z`*!rZHczeE`hdC4zaEUdBfxsbIZ zZ&7*?b0BA@S(bzgr;(e$`fCMu!zFekus42E6ECx`{7{nUTFvgrd(G8*=Eg9N3L7zw z)E~iYw %so0$Z;ZkYNXJZ_j^ zl=Fw3!Khv9&V^?C9mOZw^Ogy)ho?5PT~^8Yz{wzx8SuP;X=Bw#N8YaM^_CIb^A$L< z88pH)^ivnGM{5X7n_k0aq0(dCmZetbwWD=)LqoWNQs`2X#zm}o4xGy_H0aD|=i_KQ z|4@kKy6e3EEUlZv1= FpVbM`XJ<6$wstTnw!dQQIru_K&oTK*Ku$_U7t6&~ z?m+Id5}BK~w&on|W^v3f`q3IWEq(C}$@0ZACS^q;jd_Vv3+H+W?B*!VW^F%GP%}Rw zSJg!2aFgw|1Lb`d?XSOc^LkcsuN07$Z2dB^e_31nkwp v944 zP67UiNrHwGMItlLb$t`K$u7vZv)I~mvS5hDk|z$Qt~WcGPd*_onAtwX!Bf!QP-p33 z-XKkZbJtuOOa*;z)_2{Q!v9eBc%a794B=PXrp9y%s6FI+<=`&pnf!Qhg`I|A `a!! ZuzX()7#eG-d^zd z)Ld`q%{O;XZ(o0Ze*-hOoX-x>%{Ri@ac6dHe0+QY_~x5UPfyP<&c1hM=VtKDH*&ta zw!FN&VxVrmanpF%A`+JIuvH>$#ltq4vKJ5A71}f&b*N0sc+{z}Y{jE4ooz23bsHSh zc-&)hE#q;o#j_QU`)t0wc--&6ruk%oi&*B9i5_YzpG@*Gd--H?fScyiDIsB*Pp3wt zt$aEyrtIa@=?QI`&t{}d%X~I7W7*1QvvRh*d^Wq_nCA02CD$^aid2^7*`)Z!e$E zZ(!4Uv7kjP>&3zjwN)<`^_abSv3P=;*2^VR!m?g2osqWc<+3?tuU;-+(5Cfj#gb`R zuU4*Dw(8ZYHQQdjTD{@ef34SRwp`14y>`d5Rj=3W`S$Ad`U7m*Z#Ene%YL)*gxcyi zo6eZMezW<4oA%o+SHiO2ZoQGV`t7zmWv}0Ef6%7=ZpV{p+3$9~Sho7zt~c9WzuW!c znD+ZUU#?}p-}~d)>i7Hpe0%+VKLfkYhXX9)IUf#ksIU2Oh{ycRhr e z`F!4iUH8ic7xCOL7d_P1e!1jh{`Sk|0C(N5S3<&bzg~?wdr6^ZniL_XpVZ|9m(ip8x0L3H9}VKAkat|L5}scm2O#u7v0R z{dyz)zLeb$mUDZ*-(~){_s7Hkw)=iQ{aw57+neq0|NZ`OT>t-{FW2+`|NZfN{r`V| zzQ6zfpMhrq1G~ooCaDh%96k$}#7`Vxv-;2|(6fL=-Qyrv)Q2XCGYi #t#1*Q#Hl_#nZ;S@K_RoA#N7eB~z&%l-P$?!dE1;D5Wv z5hbaQ9Ugo9e vT;hj{7b3(cHa{DXWU4xjhUz&ImWHD8cUN$Yi1C9>=Y2eeA0^vsmu?iQ{&^KK3{8EKy+hJmDnuX+nq35+(7I zC)}()O`OoPL`B{6q*v6ZNi)tYQ8Pbz(y!{% 9AK+PL(C&FAN$T^A6F$p~rk^~MX7zdIg`Q<5 z%RSF#MSY%i bIanEx_vp&yx;j`T8`pI);yFSnT(6ij;x##(+ zTc79sIJ4aD`^o?3>wbNn&%nFFf!*sulhl_59KI`@#7|vlv-+}7pm&9fy4S_7s4t89 zq~-a(E(??{GO3Y18|`lBb!pP9FH1ChS9*n?x-@Oqm!$^1D}B )u>Z~3d<6_; n}`EHh Yry<3tbeMI$cbyPFy_9RQHFF0t?@sTIfB|PtlDU-R=L0;Vz4}>p0I2Oap9hr3D zkm!Dmqg-1K@O||P&vg;iHY`x&jd^jH!{os+6B+MY iT3O4h1iMN+CEjVKDyRj?$%p *oCu-I|PX?69e?Qc*UGwn9Q(?=D z?k?kD(0;FbI!;V;#?GFnhVhnpX?(?ZcZBy>v6%i}@wwx(&8m(^w+}JPz0GhB{(L5# zVN0@H$>Yrn*w;Sa_0dwc!*9!?^BY5U`aYK2@`FWHNRh+tKqK#rYt8a}j12(~niQTh zIGEqM)@nE 6SQhM=>;u?K|)~`n=V(*n+K&Ggg_# zgd{1%PcU7V%iEA@{hNDt+cvRTYje_>6L?q`wq5h%WoxpizI8h9+x9};cZH9mt}!Su zOTL-F6&TNZi`lSz=hNK0^wql*UR;Y>d)ziZd-Cpk*ZRzNo=#w2^H%x+hyRZ4CwaG; zUio!x%f79Le)GO-d0ur>VE sR=ZZ7E1h+}iYLdz)m)<2c*)^CuQS9e#Shtin{jrrEbGs0S1$XNx78=| zvQ+Lm{bk>WROP+z8syE_vrqauJ#{Z1gT$V-%;$ep-Iu;*F#DLpuG-I$=Knt3pJuvk zzoD^##&3lg$M>Asb#}sq_ 5xf8Q>%F+2)&V=I|2 z`+d#%@>4T-|2%pgmwL>9-}As{ubxQPE8H-P+&VYD=3%>zSZajL*GBsZMiX@SKbW2P z|5Ef*^+oZND{|alB_!m&AJbp*=h=3<<7+lf_^8hEZ=pKJ-^+~Uy}HbwI1=kQ8ta)X zvO2CAo@TFl=U#WmU0~Albm_(HpRSiTey`)T$mKs__ CkyP6=S!_Pu?UL??p- z_uLbW^LH4gJm^?Gz4^(qwlx)bd!}{otSGRu=rZ}yx;nh+fLqa|j_zGMx>F+B;uD%B zgxODel dt-o1J90v;>+sgxi?B>gsVP t2|SGKYqlGfXH?}B zlx`8=Uac`vaAqr;;>6Vw<)0%OrIMSM|7hLB# 7(K zE->fBl$;+1R~)$IJF5dFORPC3$8;K=+dehea>{;zBynZloCDq4w@>x #NrC%)pHq&XEY)OZIG|v3;oGdU zk>!gmjb5kEy1KJ?YC2b;r_t@s%1!?(jm}8UGYOxiv^e`|<~%X?BHbdlG!cdD36tKO zoc#8f(ubFBj7)h~BIld1EB`#1_ ogcdMAVtXCAK;k-Us=$=@o&p~&6>gna z@V_%V{s9MrmMc@0eEH4=&tA6f{LXE#c)HlEN@ETN6)OR@oknU^izKI4EZeY%_2MGU z#|1kp7d= TisevalG$-rm=fg_PVgXBMysxTNtZr^)fed zyL1`zL{-)Z%w#cK`b*tdVbxTI0@lYr=XzHetK6!7sM#D9z^1Ly7wIKnR%JYIdiNEH z$wujdeeT(14%|;8xHGE+N;-|rmRDV|C=_KZ*!(4oZOQ>Ifhd!e2Z|Uj nQMGu;PmbGf{oKw@4ci|ZukKqdE z&)i!QICZvXzbP-1*w|Rp(6{A7rq(MH?N@y#rY#bA*c4yD%JGr=(Tdex!qZQ530%sW zp%po6lUNS3AeY2WfooExeGAxq%O};f3tTK0czQzMyM*cdTYc{=)-g1!UO#<3iy$Yr z (WMp2eflp?Ebe#uC7#l2ek?_$*Tcy3Nr}DL%*FNYeW6dM=#O>;Q96R}O(#!U ze(%iF6;_LH6`viLj-Z`}%{z`Sea~CB3cs@Xyo33?B~zesfX+0TL+sMA8HGpRCa+;^ zSDtocQ;*4|3B`PtFBv|Zd(kOY&u6})Mu|fvMMm-O7YBxi&R?B4Cb|gSH2%mc(;D%q z+k|05^PWZR83$Ru+lRbA{QJZH|A&8@?BRF$eZ% ety9C@{ `QM_}$@$2G*KS48` zL=U~MbKl`48K!7cwt?CIt-)PmpNDNKb0)9}#wh;%-}a(eY}bS)hb{3!4+OXxZy2-} zdmXnvx3SB};9!+oOT5^24;L}HkBvSO&YCA2_dJr!PtQ8kD!(IruYT3Uj)I=Y;t!YG zT3=}@T~zJff1#ycbJAnUi8~H4Npsk9iUqdnnk0!mU*V(>qbPV lL 2BZYk(#x+&V->! z;06Oj!3<7um7K*of|X~=c72`^X~QI(WvF&}trNHVj0D5{&i`lX$}Y~oaVPyxO95*z zLxaro>OiZogDlxX# zQJnqY&33oxeZ>*Xe2%=CLH}R+EoQp1Ol<3Y!8-}Pc@GK|GVTa`VktbdIL~ycYU^vy z>Azf+Pc+t4ZaHqV
eUxrTaGtH->@YZz|4PS*a?V-yj*GJ0-?*IuoFQ>O|iUe`WsB&O|o{C}UAllh-) zJ?-C7htjsqFIcLRBVW<#vExb&(@Vj4Z4;}c7{w0Nr)#tJN8Q+=SGxJbZYSM+2d&B) z0=7ur%8={3;P%q1WI5y3bq?IKkACAk$SY-f&pN>Tz*9dBQ9GUWUguvQT2vRhce?A7 zKRpRt*%pQSJ_o$Z{Ta;uUAAn=pWb)*mtS9f@ab}6P6V6e`Hy{}YUTaBMVi95%8%Uj zlGr@0bam&()t>SO2Nx{j&Mjh0;1^KOSe$)fee~Jx%R=uh4*jp4FL1)3w^7EwU(Czh zJZR$igUiHD_{>$Vp1&rDN&07nSzExzF9lKR^DB-Ae)}|iWz7+J4n{tP2W;&f3t46T zCH|iZ_+~hB>F)#bF^yt+TPN5~_|7S8By=v}p8kx-H7<%FO;TG@-|-&Y*rwMld12+$ z&of@$IbpWSDp9Ik2$jcIzMloG3(uEZl7cgAvcM%dRO0!vQHM5v~uEwxE3zV zG>N*fe%XrE>StdEt}o#^ZD86O!5*Rcd)n2-*PO(UEnwZ{WVJ@%%sK}l+lBc@nm9XR zb5}g(XbBZ)mRRFaxb85ITk|SOhL Uc3LkjZm0&-AeSc^1P3| zN0(Jk`W7f;bFc7l-iw20OI+9P*?%L~`_}J!Kc;Q@`@q32m|Z~bgM>oQhs6Fy?jusM z4nM57@_u|>Ia`ANfBW~<7pwQ`hsfN0yTig}Q;)p8x6IzFrKSQwjyw!4zwe#r+w`X; zU_zOUf9dZ;S>2p}FG3IRI&<$^<+h2wc}~Ziq+H#;Uvw^?Z#( <`{c(l@k4yq?m68)e|7hKi^hbO$@+H>`YqpbDEp bJeo? zKfg^;j#Cufcw*nT?f>6a<^TV^QKRhtk;S)9K6B2P`iQ+?%4+Vmcf0@py}MoI&+hwg z&Uk-kmpob(`I$NCi+{v7F*p0D4|S& 3vb~LVRXj&FfzxP^`#8)1MhHwEVwv8f{?WVOS zmW1+`lx;jveN3&IXGc&t1H<_bWxSi%R=U-`E^of-?x=pfIrDq+{AbMW7g#5TC{`s@ zt(hA4WO=sThZd#s*dUE+w*zeF8Jb0$%9Tp1aw=RBBwF{BmTS9Kh!?Ru5UHM!)>!zZ zZO68nf2=VjE}SkIE#DS~SC;Ut4B>WIP}Vb}VC(-U4mB2~|G&5QZsE$1Xk<<)F;Eq_ zwz(}tJ*Lnl J%)Wk>RtD4vSzG635RH5xeSB!Ww z`|_e~CyF+6 1$BDl)$C@Euf#h zrQV|V>WoUEP_OwD6n6;J&RtfM(q6xPbHjO$zKzcsB!W`13v9L6D;|Y;cz$U<_9MaJ z1FQ5Wj)Tv8xzyVk)hh4*=x26qmwIH!_A|}=M%Kq4or%Y|WF{pi?r4>{nfyAVJNcSZ z@&(0d6Ig0j)cvw3`<>CNr^3>7Lov6k*g_-ga_U6Qm(1b+8z#zZ;@SUWBEzvsbsGI< zFPO7GD6ZMyFwY~yL37Hf?+%}iD?T~Q=CN}^49Aq-jEVdq)pAYYPri0~XHE{5u=o7H zxqrh{CPwyOU#C`bOxuuV>ku$yTFSJA0nIs*-8`JRhf_FhzbjTeW#4ELTq@bq$Qk|i zF jS84>b93PGbK# p#;btjp 6 zbMw>*SsPx=3gMi+Z>GTM4uNGi73vnuNjfnt+%oT~F#8UMP&UE2;ztir$f55aKGu2GF7;+wz=eJH} z{?E#iqQLdalRGtYdG;sH8CQ}VzAe^mv0wIO5}V`7EGO=T0c?5Gif7*PaJazAuwdQ} zgQY(zm*ihs)lucjE!EIa;VUBIoE^Yf9L2qL!lFti|78lC$rnN#kCu4u^u4#kA$&qF z6Jw}}*V39#++SS$n?A6<7B1Tmp04}N?$Z_4Dg}Y7R!f-zr%hy8U8BS`Re>X?fc5KQ zTekqGB};6dnA-asU~3QHT)1HJV=jiR4m|Bv0;e06Ra{zi;1t)i2OKpI*y0PUe;;N! z^^C1%Lhl-brDE2LOQTkBD)K&eQ~%+)c)!)+)vwlbJzTxjf%|He0AsfRi?{j)u2m}C z0`k%8w7oT6*i_ADs1}grS~HcY*;d?>ZN( QE*e6yXro@=5%l(ACN%4O;lO z=x8^q|C|FSkUEqnCu;;lJCw@ed@UZ66@3~RieBA0xjFsBEKY4E*NV+69D3d0GmD@O zWx%on3kAEs-8uF3dBDQ>mD60bwylA7D9cpC*0_b9h&{M ?>vIX}s<{hOuK#>Csx z0(aj#yL bHBXZKK>f-o}HhcUtr~K_t^8}+zb7g$@kCg-TnRDwe|J? z-B;G``T6<9Wq!W(!GBMDdiVJBeE |v)*NAXb^ep{-BXV?Zn&$ zuAplVng!fmNH+^gX@F)HZ-})DM6CeLEZz`l=daQL%`8?3b@I+y0h(E~5bWmKrSZ7e zVwp#Bli9NukNd^nbtLpVh-IoYI>~uFndoDt89#|d@8y#zAyw?E4MAZmpH4dv`!jx8 zOq=Githit7&t_ySi;S50ckaq(vkNvIRjbcCmic^c@Y$PFVp=|SKA+$4Z>f5)wn5vW zhKz^d7r3-oUcFe{c~5*&a@X1ef~8C=4+saQ&E3Gnt;4h9MyTeG%nM1jGpjB-b#K!2HM5--9*Wx%x~qHL0`2u5)@f>b*j-F`zt@CeLjJy+ z^RhL$O0KdrsD2RP;kPuKx>S&R+707=+i!cA-M5L9&YsNm J;( z)y50Dymm4NSs2boZOJ>Pcv`h+j|sQcdw1KP4rh$4KhF_1WO(DlqdQ;b&=KcWso71c zUnP1Bwy5*;_}l*2^SMu&(>tYb%~YMoTdzAZZ#Z*)|C%ID8M_&8^1{}AV$tGSp!Tps zbH7dUcTtrUx9=_VyzpP?i_!iJMdRD1;`<(+mHXJzB=BvE@OhiJB6rSN_uXzjZ}U0e zAGeItl PSNe^j{Pv5qWJrWOgF h>HGU zSWuDofXz !j2K5qF(Vej+6 zjlGtPqH@au-DD3jC*6%;)61C1@>BIwh?WP7wa#{%>IE04W~D6FTYY$*=UZNmZ!7NG z1YOjh&G3M0{scFhHP23T+7=`TYD-wE6|G((5!B(k` oPj@MQm6&cY%_OD?`Em78|7xiO(e-o+ )AYR`h4KH`o6EL+x9xox>B p-8?3h#dtFqv)$d_#5Gz>N*AeB{{z`#eweG@3=H&_ 4t4ozB~Gt2s%Bd_<3kpIo^lMk6J z$}!D~d8p` L8b?S17?&IeU3oW%c{zgeBu_>q` zzjOBn52H1=mgL<$Trk_s<@64o^No62S(9~L7y{bjBXoCf?RjCHw6L}PkC&od1&g&@ z_SW{)&$9b9n7REn2&m|rM_u~t$lz$vpPsNj{+>+cG~4f*3;`hmi?1zI?OJ(lj>JRZ z=}(egDX{gI2sHB+RA2k;;lO!w?t9raKa4ZN4ctn@&hj61X*^fLu`&L~1I3$MqSHm! zo^QPIu=!g=n}D67Kga(qmv-$bS#7oGW>dkg1kbZ!mxLBI8A%u{TBsquyl6#K=<6;X z#{bg_j}$SzKB>ESpT*N$jk2}pB)!Ti-_LN9)G`)GKJR>z#WMD=vHC_sW0eNxIX4O& zS8E2JSyx!#Abx30ZcEe@A-S(J9 z%(u<1@ynzS0R@wqd3Z!!j!bLkmVa{g zTI>gfXWts%i(E+HX}4%mIPu~6Bj2wfCJM)_SC#P@O?V*u{DjI<#dB^63s)O{xm4nz zx9_3IjEY=K&!z|JZ`mf!w>qk}ol|()#&4Aj3#9k2VAY%VkWZYiPV`XniH#i5TRUzx z%S ;rw@gB7aX{kEnpL_6z{t4 zxLZ*tnKPQjaG?XUib3(@uLV8jPJQ7T6WWa@u?tOc_gN6s`0EhcjsQC&vHay<1y(Fe zTH|C8U1E3S1FKzvzNZ4mlLM}HOSKseuv}7bX#UQ(w1CxT0*lWC_VXWV-z%gY5_YjJ z;XBB#)t=xvqnZDC0f&ZxctT6bt*iQcKbR+7;Pt=Go8!RrZ&BfiOMJeYUEd~fMin$$ zTJXM~W*M~9%xF1Z%oV;L-xzfStbZ~%^-VAr{K#o<#=k>>^U8 Nw>RAP}E&u%6Fb+0bI*IbTVw<;-6|a z! 8=YoIcRbcbLUqg5S}i zrO=^u^Jl)iZ7szMYc^lz+bY0y=||`425#*iB~Jo418&57D)3G$_Igvm@${1671t_@ za*ne<1i}?~|9s;+KY>O21FLq1<$DJncLqU~gwmNQyw+bLo~T6zO=G_!R$~8w`yE4K z*#v<_3YmQ0J9@Y4X@6j@`4DhKA!nLINQ7vkykn(xi^Js0u5UkZnmy51{@%E7I+I;P z-Dd^P
={6Vc@CI(UV-?#(+yNDcLRQBx{`_!h|9n3W<25gMqJmPOuf7;yQ z8&Sjav*g19KfNzJMGwjxOcm-5`0!8Wdb=g$_9e%qS90tgXoeqTnsgvP`U!{52CkY2 z)6xxQf*-rJ(yId`BP|6ZFMV*;Gu75C4g4UG&vB4xqFE-_iwSLyCEA@hp9Gk`zrZ_Z zi9p>_^SvoTQ@$56vjlz?;PUy9?sZc0!ji5vMJ%2ZSlkm>UYqwZB(R(|F#Vdqe_6p* z`@m#@V!p-~(_9WRY5&NZBA|2fYDcmH3xBEIdw1?U^)ik^S-&489M{`tD04U{blem& z;t}LdZLxe?z&ckXT*ZNTe?j~64Q!89s JA_JE8oDbHBI^grpfiEgSSp7gjuf>d(NaNUvEY~&{F??|E4i%hfAuxq~s$pie zg*)&3g8n|w&;{%v-62})*BllyvFx|7zM|l&(-50hQnurXx~2fveg_`iSsYoLXYYK> ze&mA1W?}9RHxdO4=ddm=P2FJ=yPc 7J@zw Z>Wrh`T%Di_E%!?M`e=>o+ za UwrrRf7tu=Cvh1*H z>p2d`s()*0ci=vq(DJ;vc-LctYX>w}{In4G%yWxrP0z =0`TPkp^sewmZ4jIq!8SqE(ED4QqOpKZ!4iRB?s>-Imj4g3xJS*jh-}=% zKG)=c>x{!(Hn06+6L>A$d8TTVg K_l*2CQ2C zuu$lImVxB)fIUZ9d{55GbQaM0!1-sFz?LSRI|;V_>|5ARJI`5^@UU%*+S6@ +X-+zS>X5n|2MuT z7kHmk7q1A}rQuMf)MmLqz-G(OH9C*Ci`<@Qe6{*aMm9_14pwFc`PCa2dBt9Nko~n_ z>C)LuRRKGn7wlY+l>5I(WB&s8{U7%6Y-EaR=;2RZ;{IXDYB$pz&YU)<7n&t 95K=l1CwGvl>q zo|9fbZTmXrOOE})rgbVdzjy8B{>a!rfp3mf rV ^ lgIPV_V;Bg~J?Xp188@|Bri7}m8j!&wRKg^9kFnvaI-u;sT z#e$n>PO0N-In_GBllh2yYQTYAo0t~4Sub=D?qJGeR N_SC*PUC@{ap7-+v7)9vSU=YpbWpttJ=p-nH?co?!NG_r?!g{C8^-d^i{uZm^kY z%*LBi{^r8s&kmdnkM&wt3$k%#CoP-Zx@k+SiFHi~_gvxPS3TUZE}Jv c7gC+K~Y}*bDIj!CwE-3N!W2` z8+T! 2`ZDe9eUirp>yKfp_ zQ$p}FCy{9eC3g?RAKRmA^||ow{{=HWA5?AI9(2?^FNS?z%> HFM3?<$s9k?S z@YPwmZ!EmO1#rIjw0-r c5 z`2x{eff{_*SzY7v;}emnnR11ZVQ_uaATcB zn%4|w*9+Q3H&?Q+SY`c+fjxiP-o4LC1su6={D1M7QCy3Gf&J3=cB$=Vif(fqb;Xw* zV=8!1^+SPU?vwjYTWq|)X{tYneQ<>@{8Y%H<{kbSNg*mM8xzj{aNyx#aVz@p{6_${ z%eP}14ormw+m3u-j(AbSz1#JN17ETOQ+TG^g${qtVkXZI-TkS&TipW7+DrH*^W_vg z?TMSi!;`VlAkWU#>F?fWIrr`{JYarfVR!#+P-)zenk+9qX4g;2NfO3vSMC%Y5wOqt z%{kNA>drQk#m#oCJ6@a+YG&T@?y(C~dxFE$!(46$?k@;pONcQ&bfehmiGh=a_=nA0 z5032Hl&!TYuS%75uVOJvl>?8MiSv&I4{Hty*f~@&wY1FtKVjS4Eo@V{uBpZOXiwmX z{>4`{Z!6msUg6L6-5)vRPwq22a(Al3ev6s5hl+HzGqv#k-2Ei(-LgGL`rPJLEOvZ* z?v$z`%T!Ok(+TEflZ88<=Y6@!*Yu&x;C1nngf(*JS#M5TdOl#=uaJ~{uk^`>{G*4M z&hrKS-oWGbnE8auOvOs)WY!%o1!}xF?_b{FbCr4VC1Zg}w&j-{zMY>Sd`aQ^1XhO2 z1zpC08aWgA{&8GsI>6$T@cyJ$@@BTZ|E97;l{j9$Fz0y?+okCRAB*Q6)vUN#&NsDy zRdYt;nH19>(`;*6nY`zw6ncFPJ%9aeFH^yV?CDRAzd5(=UEW`2rf+O(qip}r61HGB zoZqn5Y~!|wZ{PmAGiBZU$ogfXz+_*E_(QB=>|!OJvBzE5OIApDZ~VI9hkN2x2E`R~ z_m+SA5O}$$$(2h)!{_L1aY6Zpj&BccZWb40I9mDsgQs@0(ov5#t&jx^j=YO??PKLm z>e=z;ROdvEkP8P|`Qr~3w+3u**x1y%v`qAE z!`5k-)!p@Y6l_x?aK2V>=aj_oZMk>%wMvJ-i`iBB_TJIi;qT-2)&Bj*DkEVK!4SvI z{nE@-$y~}l`;n?l e)9l zPm>{xp`o`dW;PeY%@sx~rV0BsX>XeE)p&AV@h*>S&4X?#8(oC`T?}1C!&5FEIknHp zQJ}v}DP2u;!sWAiC(Bw4*PrT};kB{DpgDJ5*)qY@-eXs!GFP1O^X^`)msOZ6H#cFk z`z#kN<O-2Cj)LQa>t=Z9i+Vnq9`b@9+mV5xa*x`(Ey1Qs_*5q 3UT-Yq9iOg|U-w5!c$S0G1{(W6d&F~0|0te#y(ol~!f zed5|_n01i-(#Hpt%U9&tSsq%lLnXP<*}>lD%SN$!R ?m;nc63X?x!Z%D$Olxk``*X-WQ`LYTvS;kwxu&@HrvB%|5={r86yDW;J~>Tp=6S zm%zw#+akMNR7x)-OItyxvTZ@no6P4jVKWjK4+}G>ImWI!C&_$SI#i>lKUY4beU7Q# zm%Qk6J$EYR^`5AFHr;FA0!EE5B6m)xiM6b7k!se?VxMjlwNKE#$2If$sgj_~8%ci{ zBGS~GZU{a(b|oZ%kwGlu!{h(wCVH;BVmMpp*{@@Ao(T&r=YCLp&i&_#m09lA>uI}l zw@20(8cAi`;BbyFT~lV1l{iIe>x#v8yVcmZg*h3P@u+)h>}L$%`gmMVY*mj9iy#Z1 z+AkZcXS=3G=jkqp__2uZ)Yk~+j*P8RoyC(U{9cv*C(b=)$pVS}0+#Ff){EKSOimM8 z(-ye6k;g*gJ9D2(o%{E%2Y=W7dgL1Y-Rjo*pbxwJrFL~RSC};zd|%#bkh *@Pzzu)(_es}HT am)z>HHfp=GOg~$N9?PwF{~HaOC*EH)ZTEk{XZ2Z}cOLL9cUmU1 zwqU-rBJ1+T~&OZd3hX`dgfbckt0-> zWO0})$gqhuW?qWd3x_Kk1azWXl!Rp^91X%==Xd`;bunaxE8nKT&KE|Hgo;ZJD;KqP zmj6za`?|y3PVBX7pfe+9jX|4fPogLHo%w?6Q(fGS3AXgMq^PJ`dU|Dj=$d*YCDkb8 z_`5qB`{%QyYPkNKZojCFbE`$W?TJJ#sXg!NJwj5Hj8C3$KX+-`_9Ll!rIrVy3pe@C zlSwsbwLFtJZ4&F&1*iOc1lMLBoHTvYom7*hmcChG!jt#@?~#mtXW^byc6s*wn)}^3 zpIDU&wK&ZWDaR`9^vK?JdG7m`bd!yqJJ#h0&-s2N-R|*p9hPg1P8%2xi2E7nR;?1A z^vfl~Npx30o0*8~r`=53eXjK}tbDKcx+KHRbXQ YbK zD`?ubD@(IBi*dCiaAux)EZLXgvwOkJ?bE+qS#Hmo74SMpw{=TTp3kk9tgX93mYH2$ zDQT)~EV@bIxaw4Q|D#!9TX$Xcf3~)Jh2sL(kJmibO}n}#-SzdxJ+s_i{ye|R&^J5g zsa5!{cS|SbUyY9ax+{F&wXf^Wor#ZUwT?KH^mV3Oj7 zS!jFQ?p4XAyCct;u`F(w8<+ZZyYrbtTQ@IveH&-reM7czvY(e~u9*Ams9V>*ZR|{4 zE9ScT 4YyPnkBcznak4V|*uUNm Cm3hXYcKJ}#sBzgW &n4!iMV?0xIZ-&s!8+sXS z?Xw 3w6FY+VKi=MoQ|xpNkW{n%@=IMp_L zzr`-S3DdqrYvjI^S;Vm0$MNb4ZjaEL3$+5xEE)eB96h9#)Wya+yIo_;Cxb~x6fK$r zyxwQ*&CT=wHna9%>7u|5*{pf0S;ePUo_@h&UDAFlHs0~ q9Hy9P^dv%CMut9&)Rz`sgsn%a)=brU#v^&g9`UeI!rsr=C5QbnOH z5p0HaA3H4=6iX$T7=(2KJKZ@PIodmp>em&{i@Mb%BvI+skhA?w#F`cfyARRph3&p9 zygf^g_xjhvq8iKG=QL}5ywQ v)BIw|!^kUwieqlA8@ zyId8k*wY0KVgd(`@}CnEneVzt;k<{F=uzL2efqav6g%#UKH)oQcEBSUld5Az^9%)= zd~96;g%4?c5Hu-L=#>uq>3Z+%S+ 7Ebj1mY%%9u z^_!i4%XhNu({1*Tm1S$rM>P0v!Vne!@CJ*jjm5> zGJUZ_`N)bE1(}7d85d`=p5Ni2lfmF5azs)2N&-{ed!6cU{4>85Hf~XV ^&md{L8O1 *oU+HTnWI+`0i$VvE;zyy`H@ zn9;VNG5$bDe1_x|hK-^Znw%|I92uB=og4q_TeR2SXxHx)Gf3nr-N+sj(a{=lAa4iL z+5r7TWf_JUZ09_j{B9rS?NQJSaS>eX^p%NQ`v`}ru$5@W=5h_@yFWXheGriMY1A{C z-`y$cXwXsofpO+cJBA46@&oOe61~ng8&60asnl>WusWiivd;C0>-#JhYwJzTmMt!t zD+;SECLS;sQD{DVm?LvVi=GA3g4auCWcHO!V6%7Fz|72$RpMBx!0|4DBYckf-G|22 zGmeTzHNL85FLH2sed=iGlXbBn(|Rv7RUGJuf76$6$25sWG$f`iqOh-|f-S0oMe>iT zNCfMa!;^bE*=3m-ns&^|J9FUlO!a8a3s^E0*w5T(FHdOq7hnqAEE{%1;zNZ%b0vFz;6^ b=q5S{ypNuDOcXkPOIxBP( zS9n` DP=h#h_^ZQnvJ!D~hFMFw2H4noA zp|>7QZ94>-D%rv&%o5;UBGSOTb~E!6g+?Qb_K1UrGB!#dd8yP>>33<#@<}E_YgIZk z1ewY@PWmex=Hfcf`BwXS;5_c57fw1gx@_oj{vrN=M C5`Vt2>frfY22&Q zaV6Vuy+up00=w#I=Y>6f{}LNL9A=btu%sDrD%?0;K7%c<=YXnfh?;JXMrw%qTpghY zz4;Cjr9W8he>CY|U^3s(*Q3Dh)e+EB==eaP&FTk}gK~37Li HxerVQC zXwr^g-g1P!Yyyi>$eDJN3zt%uW=rl1& `OMxjh)NB%tf8`qurGYOc~O4c@NmmWU%}D&iV4wJ1z9af~-sFsaG;w#m;V=s=a{q zzg=}_PX>E-2kX1d8~uK9dPVHfdf8FFfGyafPgPq+YDG(WLSW0mP3ZzGts7>uUI{lV z={_N_P`iV*=mr1kiZ xf%!+7l4eF@X^_?_J`$V%>LTf| 30c4z_JhT-z^CT&O0h*Q77JQd^s;(ym~(!jk75I}ML~0zmSbk?_Kd&| zd5LBh1J |y3?H_S2@+XxUauG!ecd?S!;o2Qr9`g?v&Y^ zuPhK2{d?xWewG456SvyKyWA_~8I%skNBJJ}y!-6yidhMF)p@79Tzc=7Dc9?z6BI7S zz1e$D>EXTibMKuK5c+5_S(f?!XV?2*LhpY~z5lKB{`c1VKc?RQx%B?8t@nQ)z5nOx z{l9