# О проекте ## Описание **Kopycat** — это эмулятор аппаратных платформ для создания виртуальных копий физических устройств различной архитектуры. Основные особенности: - Простота сборки нового устройства. Настройте свою платформу с помощью Kotlin. - Лёгкая кастомизация. Создавайте собственные модули платформы на Kotlin. - Кроссплатформенность. Kopycat использует JVM в качестве основы и может работать на Windows, Linux и OSX. - Полное соответствие. Представление виртуальной платформы идентично схеме эмулируемого устройства. - Поддержка множества архитектур: MIPS, ARM, MSP430, v850ES, x86_64. - Режим пользовательского уровня. Возможность эмуляции отдельного ELF-файла без полной эмуляции системы. Проект включает: **Ядра процессоров:** ARMv6, ARMv6M, ARMv7, MIPS, MSP430, v850ES, x86_64, PowerPC (E500v2) **Микроконтроллеры (MCU):** Cortex-M0, STM32F0xx, MSP430x44x, PIC32MZ, P2020, Atom 2758, ElanSC520 ## История возникновения Началом проекта послужила задача эмуляции устройства с нераспространенной архитектурой процессора. Существующие платформы для эмуляции аппаратных устройств (в частности, QEMU) не поддерживали эту архитектуру. Для решения возникшей проблемы было два пути: 1. Дополнение существующего эмулятора. 2. Написание собственного. Было решено идти по второму пути. Помимо эмуляции целевой архитектуры у проекта Kopycat были и другие цели, направленные на упрощение эмуляции специфичных устройств, а именно: - *Простота разработки*: для того, чтобы обеспечить удобство при добавлении новых архитектур и модулей, был выбран язык программирования Kotlin, обладающий большим количеством удобных синтаксических конструкций. - *Расширенные функции взаимодействия с эмуляцией*: в Kopycat присутствуют инструменты, позволяющие гибко взаимодействовать с самим процессом эмуляции. В частности, так называемые "трейсеры", с помощью которых можно описать логику, выполняемую при исполнении каждой ассемблерной инструкции устройства. Например, построить стек вызовов функции программы без исходного кода или даже изменить логику работы эмулятора. - *Интеграция с другими инструментами*: для того, чтобы Kopycat можно было использовать в паре с другим ПО, в нем были реализованы протокол удаленного вызова процедур (RPC) и REST API. Безусловно, JVM в совокупности с использованием трейсеров и других дополнительных функций эмулятора сильно сказывается на скорости его работы. Поэтому еще одной важной функцией является создание "снапшотов" - снимков состояния работы, с помощью которых можно возобновлять работу эмулятора с определенного момента. ## Архитектура Kopycat имеет модульную архитектуру, благодаря которой можно быстро создать эмулятор произвольной аппаратной платформы. Модули образуют иерархию компонентов, где эмулируемое устройство называется топ-модулем. Взаимодействие модулей друг с другом обеспечивается шинной архитектурой устройств. Шины, в свою очередь, подключаются к модулям через порты. Пример простейшей архитектуры: ![](images/simple_device_example.png) Такой подход позволяет при разработке устройства описывать его архитектуру идентично его физической блок-схеме. Сам топ-модуль инициализируется в экземпляре класса Kopycat. Для управления эмулятором предоставляется Kotlin-коносль. Также доступны протоколы GDB, REST и RPC. Для того чтобы обеспечить возможность подключаться к системе эмулируемого устройства используется экземпляр класса UartNetworkTerminal. В целом, виртуальное устройство и его взаимодействие с основной операционной системой можно представить следующей схемой: ![](images/kopycat_top_structure_scheme.png) ___ # Руководство пользователя Целью данного руководства является обучение запуску и использованию эмулятора *Kopycat* "с нуля". Краткий обзор: - Первым шагом является получение дистрибутива Linux, который будет грузиться в эмуляторе. В разделе описывается сборка дистрибутива при помощи утилиты Buildroot. - Далее идет описание процесса развертывания эмулятора для того, чтобы можно было начать работать с ним. - И наконец, после запуска эмулятора, описание того, как происходит работа с сетью (E1000) и диском (SATA). Раздел позволит расширить навыки использования *Kopycat*, в частности, работать с сетью и передаваемым с хоста диском. >**_Примечание:_** В руководстве подразумевается, что вы уже клонировали проект *Kopycat* на ваше устройство. Если нет:`git clone https://github.com/inforion/kopycat.git`
Скриншоты Kotlin-консоль: ![Kotlin-консоль](images/kopycat-console.png) Терминал socat: ![Терминал socat](images/terminal-socat.png) Сообщение в консоли о подключении gdb-клиента: ![Сообщение в консоли о подключении gdb-клиента](images/gdb-connected-log.png) Отладка через IDA: ![Отладка через IDA](images/gdb-ida.png)
--- ### Предварительные требования Для использования Kopycat убедитесь, что у вас установлены: - **Java Development Kit (JDK) 11** - **Socat**: Для взаимодействия с терминалом - **Docker или Podman** (опционально) --- ## 1. Подготовка дистрибутива при помощи Buildroot **Buildroot** — это инструмент, который упрощает и автоматизирует процесс сборки полноценной Linux-системы для встраиваемых устройств, используя механизм кросс-компиляции. Настроить toolchain и конфигурацию ядра можно командами `make menuconfig` и `make linux-menuconfig`, предоставляющими графический интерфейс. Сборка происходит выполнением команды `make` После конфигурирования и сборки, артефакты будут лежать в директории `.\output\images` Подробнее про возможности Buildroot в официальной документации - [Buildroot - Making Embedded Linux Easy](https://buildroot.org/docs.html) ### Сборка ядра для kopycat x86 (Version 0.11.0+) В директории `.\kopycat-modules\tops\demolinux\src\main\buildroot` находится ContainterFile для сборки образа с Buildroot со сконфигурированным ядром Linux для x86. Для сборки можно использовать Docker или Podman (команды аналогичные). 1. Переходим в нужную директорию: `cd .\kopycat-modules\tops\demolinux\src\main\buildroot` 2. Собираем образ: `docker build -f .\Containerfile -t my-buildroot .` 3. После того как образ был собран, вытаскиваем из него ядро и root.cpio: ``` docker create --name temp-container my-buildroot docker cp temp-container:/build/buildroot/output/images ./images docker rm temp-container ``` Образ ядра и файловой системы будут лежать в директории `.\kopycat-modules\tops\demolinux\src\main\buildroot\images` Для сборки ядра под MIPS аналогичные шаги в директории `.\kopycat-modules\tops\demolinux_mips\src\main\buildroot` ### Сборка ядра для kopycat x86 (Version 0.10.0) В директории `.\kopycat-modules\tops\demolinux\src\main\buildroot` находятся архив с buildroot (`buildroot-2023.11.1.tar.gz`) sh скрипты для конфигурирования и сборки. 1. Распаковываем архив с buildroot: ``` tar xf buildroot-2023.11.1.tar.gz mv buildroot-2023.11.1 buildroot-unpacked ``` 2. Запускаем скрипт для сборки `./Build.sh` 3. Образ ядра и файловой системы будут лежать в `.\kopycat-modules\tops\demolinux\src\main\buildroot\buildroot-unpacked/output/images/` >*Примечание*: Для Windows можно использовать WSL или Docker в качестве виртуального окружения для запуска скриптов и сборки ядра --- ## 2. Развертывание эмулятора ### 2.1 Руководство для Linux #### Шаги для запуска и проверки Demolinux с Kopycat: 1. **Проверьте версию Java** Убедитесь, что используется Java 11: ```bash java --version ``` Если установлено несколько версий JDK, установите переменные окружения `PATH` и `JAVA_HOME`, указывая на JDK 11. 2. **Соберите kotlin-extensions** ```bash git clone https://github.com/inforion/kotlin-extensions.git cd kotlin-extensions ./gradlew publishToMavenLocal ``` 3. **Откройте проект и соберите Kopycat** ```bash ./gradlew createKopycatConfig ./gradlew buildKopycatModule ``` 4. **Установите socat** ```bash sudo apt install socat ``` 5. **Запустите эмулятор с помощью скрипта** Перед запуском эмулятора, убедитесь, что у вас есть все необходимые для работы ресурсы (ядро и rootfs для demolinux). Они должны находиться в директории `./kopycat-modules/**/src/main/resources/**/binaries` модуля или в `. /kopycat/resources/**/binaries` (если их нет, создайте их по инструкции в [первом пункте руководства](#1-подготовка-дистрибутива-при-помощи-buildroot) и перенесите в директорию `./kopycat-modules/**/src/main/resources/**/binaries`). Первая директория будет использоваться, чтобы положить ядро в jar во время сборки, а вторая, чтобы получить его во время выполнения программы. Запустите скрипт: ```bash ./kopycat-private/temp/config/bash/demolinux-default.sh ``` >**_Примечание:_** Также, стоит учесть, что для demolinux_x86, например, Kopycat по-умолчанию ищет ядро и rootfs как "bzImage.gz" и "rootfs.cpio.gz". Если вы хотите переопределить названия ресурсов (например, чтобы использовать их без сжатия), вы можете добавить их в параметры топ-модуля в команде запуска программы `-p "...,bzImageName=bzImage,initRdName=rootfs.cpio"` >**_Примечание:_** Ядро, предоставляемое в процессе выполнения, имеет больший приоритет. 6. **Запустите эмуляцию устройства в Kopycat** В консоли Kopycat выполните: ```bash kc.start() ``` Если у вас есть **снапшот**, в котором система уже запущена и готова к использованию, загрузите его, чтобы избежать ожидания и сразу получить доступ к консоли: ```bash kc.load("snapshot_name.zip") ``` >**_Примечание_**: Снапшоты (снимок состояния) позволяют сохранять состояние устройство и загружать его, если вам > нужно перейти к этому состоянию. Чтобы создать снапшот, используйте команду `kc.save("snapshot_name")`. > Снапшоты по умолчанию находятся в директории `./temp/demolinux` (для demolinux). Если вы хотите загрузить снапшот, > убедитесь, что он также находится в этой директории. >**_Примечание_**: Директория снапшотов определяется в команде запуска эмулятора опцией "-w". 7. **Подключитесь через socat** ```bash socat rawer,escape=0x0f tcp4:localhost:64130 ``` 8. **Проверьте работу Demolinux** После того как система загрузится и появится доступ к консоли, введите следующие команды и дождитесь вывода: ```bash ls -l cat /proc/meminfo ``` --- ### 2.2 Руководство для Windows #### Шаги для запуска и проверки верхнего модуля Demolinux с Kopycat: 1. **Проверьте версию Java** Убедитесь, что используется Java 11: ```powershell java --version ``` Если установлено несколько версий JDK, настройте `PATH` и `JAVA_HOME` на путь к JDK 11. 2. **Соберите kotlin-extensions** ```powershell git clone https://github.com/inforion/kotlin-extensions.git cd kotlin-extensions ./gradlew publishToMavenLocal ``` 3. **Откройте и соберите Kopycat** ```powershell ./gradlew createKopycatConfig ./gradlew buildKopycatModule ``` 4. **Установите socat** Есть разные способы установить/собрать socat под Windows. Например, вы можете установить его при помощи терминала msys: ``` pacman -S socat ``` Опционально, добавьте путь к socat в Path, чтобы можно было вызывать его из Powershell 5. **Запустите эмулятор с помощью PowerShell-скрипта** Перед запуском эмулятора, убедитесь, что у вас есть все необходимые для работы ресурсы (ядро и rootfs для demolinux). Они должны находиться в директории `./kopycat-modules/**/src/main/resources/**/binaries` модуля или в `. /kopycat/resources/**/binaries` (если их нет, создайте их по инструкции в [первом пункте руководства](#1-подготовка-дистрибутива-при-помощи-buildroot) и перенесите в директорию `./kopycat-modules/**/src/main/resources/**/binaries`). Первая директория будет использоваться, чтобы положить ядро в jar во время сборки, а вторая, чтобы получить его во время выполнения программы. Запустите скрипт: ```powershell .\kopycat-private\temp\config\powershell\demolinux-default.ps1 ``` >**_Примечание:_** Также, стоит учесть, что для demolinux_x86, например, Kopycat по-умолчанию ищет ядро и rootfs как "bzImage.gz" и "rootfs.cpio.gz". Если вы хотите переопределить названия ресурсов (например, чтобы использовать их без сжатия), вы можете добавить их в параметры топ-модуля в команде запуска программы `-p "...,bzImageName=bzImage,initRdName=rootfs.cpio"` >**_Примечание:_** Ядро, предоставляемое в процессе выполнения, имеет больший приоритет. 6. **Запустите эмуляцию устройства в Kopycat** В консоли Kopycat выполните: ```powershell kc.start() ``` Если у вас есть **снапшот**, в котором система уже запущена и готова к использованию, загрузите его, чтобы избежать ожидания и сразу получить доступ к консоли: ```powershell kc.load("snapshot_name.zip") ``` >**_Примечание_**: Снапшоты (снимок состояния) позволяют сохранять состояние устройство и загружать его, если вам > нужно перейти к этому состоянию. Чтобы создать снапшот, используйте команду `kc.save("snapshot_name")`. > Снапшоты по умолчанию находятся в директории `./temp/demolinux` (для demolinux). Если вы хотите загрузить снапшот, > убедитесь, что он также находится в этой директории. >**_Примечание_**: Директория снапшотов определяется в команде запуска эмулятора опцией "-w". 7. **Подключитесь с помощью socat** Выполните: ```Powershell socat rawer,escape=0x0f tcp4:localhost:64130 ``` >**_Примечание_**: Мы используем socat с режимом rawer, чтобы отключить эхо и передавать управляющие символы > эмулируемой системе. Если по какой-либо причине вы не можете запускать socat в режиме rawer, попробуйте `socat pty,raw,echo=0,iexten=0,isig=0,ixon=0,icanon=0,min=1,time=0,escape=0x0f tcp4:localhost:64130` 8. **Проверьте работу Demolinux** После того как система загрузится и появится доступ к консоли, введите следующие команды и дождитесь вывода: ```bash ls -l cat /proc/meminfo ``` --- ### 2.3 Руководство по запуску Kopycat в контейнере Docker Вы можете запустить Kopycat используя Dockerfile в репозитории проекта 1. **Убедитесь, что у вас установлен Docker** ```bash docker --version ``` 2. **Соберите Docker-образ** Dockerfile находится в директории проекта. ```bash docker build -t kopycat . ``` 3. **Запустите Docker-контейнер** Чтобы запустить контейнер, выполните команду: ```bash docker run -it --name kopycat-container kopycat /bin/bash ``` Рабочая директория контейнера будет иметь следующее содержание: ``` opt/kopycat ├── demolinux-default-net.sh ├── demolinux-default-x32.sh ├── demolinux-default.sh ├── kopycat-modules // runtime scripts └── production // JARs ``` Если вы хотите использовать свое ядро или снапшоты, вы можете использовать volume: ```bash docker run \ -v ./temp:/opt/kopycat/temp/demolinux \ -v ./pathToTheResourceDir:/opt/kopycat/resources/ru/inforion/lab403/kopycat/modules/demolinux/binaries \ -it --name kopycat-container kopycat /bin/bash ``` Первый volume содержит снапшоты, а второй это директория с ядром, rootfs и так далее. Вы также можете использовать `docker cp` вместо volume. 4. **Запустите Kopycat внутри контейнера** ```bash ./demolinux-default.sh ``` 5. **Запустите процесс эмуляции в Kopycat** В консоли Kopycat выполните: ```bash kc.start() ``` Если у вас есть **снапшот**, в котором система уже запущена и готова к использованию, загрузите его, чтобы избежать ожидания и сразу получить доступ к консоли: ```bash kc.load("snapshot_name.zip") ``` >**_Примечание_**: Снапшоты (снимок состояния) позволяют сохранять состояние устройство и загружать его, если вам > нужно перейти к этому состоянию. Чтобы создать снапшот, используйте команду `kc.save("snapshot_name")`. > Снапшоты по умолчанию находятся в директории `./temp/demolinux` (для demolinux). Если вы хотите загрузить снапшот, > убедитесь, что он также находится в этой директории. >**_Примечание_**: Директория снапшотов определяется в команде запуска эмулятора опцией "-w". 6. **Подключитесь при помощи Socat в Docker-контейнере** ```bash docker exec -it kopycat-container bash socat -,rawer,escape=0x0f tcp:localhost:64130 ``` >**_Примечание_**: Мы используем socat с режимом rawer, чтобы отключить эхо и передавать управляющие символы > эмулируемой системе. Если по какой-либо причине вы не можете запускать socat в режиме rawer, попробуйте `socat pty,raw,echo=0,iexten=0,isig=0,ixon=0,icanon=0,min=1,time=0,escape=0x0f tcp4:localhost:64130` 7. **Проверьте работу Demolinux** После того как система загрузится и появится доступ к консоли, введите следующие команды и дождитесь вывода: ```bash ls -l cat /proc/meminfo ``` --- ## 3. Проверка работы сети (E1000) и диска (SATA) в эмуляторе (demolinux x86) ### 3.1 Диск Создаем диск в корне проекта `fallocate -l 30M disks/demo.bin` >**_Примечание:_** Обратите внимание, что fallocate может не сработать, если вы пытаетесь его использовать, например, > в файловой системе хоста в WSL. В этом случае создайте файл в директории /tmp или /opt и перенесите его в > директорию disks в корне kopycat Можно проверить, что эмулятор видит диск как устройство: ``` $ fdisk -l ``` *Вывод:* ``` Disk /dev/sda: 0 MB, 65536 bytes, 128 sectors 0 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/sda doesn't contain a valid partition table Disk /dev/sdb: 30 MB, 31457280 bytes, 61440 sectors // Наш диск 3 cylinders, 255 heads, 63 sectors/track Units: sectors of 1 * 512 = 512 bytes Disk /dev/sdb doesn't contain a valid partition table ``` Далее, требуется создать на диске таблицу разделов и один раздел ext4. Удобнее делать это на хосте (или в WSL для Windows). >**_Примечание:_** При создании таблицы разделов на хосте стоит выключить эмулятор Привяжем файл к устройству: ```bash $ sudo losetup -fP --show ./demo.bin ``` Должно вернутся имя устройство, например, /dev/loop0 Создадим таблицу разделов: ```bash $ sudo fdisk /dev/loop0 ``` Последовательность ввода в интерфейсе fdisk: ``` o очистить старую таблицу и создать DOS n новый раздел p primary 1 номер 1 первый сектор – последний сектор – (весь диск) w записать и выйти ``` Форматируем раздел и создаем файловую систему: ```bash $ sudo mkfs.ext4 /dev/loop0p1 -L GUESTDISK ``` *Вывод* ```bash mke2fs 1.46.5 (30-Dec-2021) Creating filesystem with 7672 4k blocks and 7680 inodes Allocating group tables: done Writing inode tables: done Creating journal (1024 blocks): done Writing superblocks and filesystem accounting information: done ``` Отвязываем устройство: ```bash $ sudo losetup -d /dev/loop0 ``` Теперь, когда на диске есть отформатированный раздел, можно попытаться смонтировать его в эмуляторе и проверить работу В эмуляторе: ```bash $ partprobe /dev/sdb sdb: sdb1 $ mkdir data $ mount /dev/sdb1 data EXT4-fs (sdb1): mounted filesystem with ordered data mode. Opts: (null) $ cd data $ echo test test test > testfile $ cd .. $ umount data && sync ``` В диск был записан файл testfile с содержимым "test test test". Можем убедиться в наличии файла и его содержимого вновь на хосте (или WSL): ```bash $ sudo losetup --find --show --partscan ./demo.bin /dev/loop0 $ sudo mkdir /mnt/testdisk $ sudo mount /dev/loop0p1 /mnt/testdisk $ ls /mnt/testdisk lost+found testfile $ cat /mnt/testdisk/testfile test test test ``` ### 3.2 Сеть Для проверки работы сети требуется поднять виртуальный TAP-интерфейс на хосте на порту, указанном в параметрах запуска эмулятора. >**_Примечание:_** Поднятие TAP-интерфейса нужно выполнить **ДО ЗАПУСКА ЭМУЛЯТОРА!** ```bash sudo socat tun:192.168.19.2/24,tun-type=tap,iff-up,iff-no-pi tcp-listen:30003 ``` В Windows команда socat остается неизменной, хотя запускается в WSL, однако предварительно требуется выполнить проброс портов. Узнаем ip-адрес wsl в подсети хоста: ```Powershell PS wsl hostname -I 172.27.181.15 ``` Прокинем порты, используя адрес, полученный на последнем шаге (Powershell): ```Powershell PS netsh interface portproxy add v4tov4 ` >> listenaddress=0.0.0.0 listenport=30003 ` >> connectaddress=172.27.181.15 connectport=30003 PS New-NetFirewallRule -DisplayName "WSL PortProxy 30003" ` >> -Direction Inbound -Protocol TCP -LocalPort 30003 -Action Allow ``` После чего можно поднять сеть приведенной ранее командой socat. Для теста поднимем также python http.server в WSL в директории с каким-нибудь тестовым файлом: `python3 -m http.server` Наконец, проверка работы сети в эмуляторе. >**_Примечание:_** Некоторые команды требуют ожидания Поднимем сеть eth0: ``` $ ip link set eth0 up ``` *Вывод:* ``` IPv6: ADDRCONF(NETDEV_UP): eth0: link is not ready e1000e: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: Rx/Tx IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready ``` Назначим IP-адрес: ``` $ ip addr add 192.168.19.10/24 dev eth0 ``` Попробуем скачать файл test с хоста: ``` $ wget http://192.168.19.2:8000/test ``` *Вывод:* ``` Connecting to 192.168.19.2:8000 (192.168.19.2:8000) saving to 'test' test 100% |********************************| 10 0:00:00 ETA 'test' saved ``` Убедимся, что содержимое скачанного файла совпадает с содержимым этого файла на хосте: ``` $ cat test ``` *Вывод:* ``` test file ``` ### 3.3 Глобальная сеть Если вы хотите подключиться к глобальной сети, потребуется выполнить несколько дополнительных шагов. Мы настроим NAT между интерфейсом TAP, созданным в предыдущей части для связи эмулятора и хоста, и интерфейсом, который используется для подключения к глобальной сети на хосте (WSL для Windows). Сначала необходимо включить IP Forwarding. Добавьте следующую строку в файл `/etc/sysctl.conf`: ```Bash sudo sysctl -w net.ipv4.ip_forward=1 ``` Далее нужно настроить NAT с помощью iptables: ``` sudo iptables -t nat -A POSTROUTING -s 192.168.19.0/24 -o eth0 -j MASQUERADE sudo iptables -A FORWARD -i tap0 -o eth0 -j ACCEPT sudo iptables -A FORWARD -i eth0 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT ``` *eth0* здесь это интерфейс для доступа к глобальной сети. Вы можете определить свой интерфейс с помощью команды `ip addr show` Теперь нужно настроить маршрут по умолчанию в эмуляторе: ``` $ ip route show 192.168.19.0/24 dev eth0 scope link src 192.168.19.10 $ ip route add default via 192.168.19.2 dev eth0 $ ip route show default via 192.168.19.2 dev eth0 192.168.19.0/24 dev eth0 scope link src 192.168.19.10 ``` 192.168.19.10 - адрес хоста в созданной в предыдущем пункте сети. Наконец, попробуем связаться с 8.8.8.8 (Google public DNC): ``` $ ping 8.8.8.8 ``` Получим: ``` PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=100 time=4.000 ms 64 bytes from 8.8.8.8: seq=1 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=2 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=3 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=4 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=5 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=6 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=7 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=8 ttl=100 time=0.000 ms 64 bytes from 8.8.8.8: seq=9 ttl=100 time=0.000 ms ^C --- 8.8.8.8 ping statistics --- 10 packets transmitted, 10 packets received, 0% packet loss round-trip min/avg/max = 0.000/0.400/4.000 ms ```