D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Emproof Nyx - продукт для усиления безопасности встроенных систем через инновационные техники перезаписи двоичных файлов. Он защищает софт от реверса и эксплойтов с помощью статической инструментации бинарей, добавляя такие меры защиты как обфускация кода, анти-дебаг и анти-тампер чеки, а также митигации эксплойтов типа стековых канареек и контроля потока выполнения.
Перезапись бинарников не зависит от компилятора и языка программирования, что даёт гибкость в модификации и хардении кода. Процесс включает три шага:
Изначально использовались Capstone для дизассемблирования и Keystone для ассемблирования, но эти фреймворки оказались не идеальны. Keystone часто выдавал некорректный машинный код и плохо сообщал об ошибках. Также в нём не имелось поддержки меток в ассемблере.
В результате был создан Nyxstone - библиотека для сборки и ревёрса на базе LLVM. После года тестирования на разных архитектурах, он стал открытым под MIT лицензией.
Nyxstone можно использовать как отдельный инструмент или как библиотеку для C++, Rust и Python.
Еще один юзкейс - более гибкое размещение последовательностей инструкций. Пример - реализация теневого стека (для защиты адресов возврата от атак):
Код: Скопировать в буфер обмена
Nyxstone может ассемблировать такие снипеты отдельно, если указать адреса отсутствующих меток. Для патча на 0x1000 нужен адрес
Для этого юзкейса важно, чтобы сгенеренный машинный код корректно переходил по пользовательским меткам и ссылался на них. Иначе вставленные в бинарник инструкции могут уйти не туда, что приведет к неправильному потоку выполнения или инвалидным обращениям к памяти.
При разработке Nyxstone обнаружилось, что в некоторых случаях LLVM генерит код, который ссылается на адрес
Чтобы Nyxstone не выдавал неверные байты инструкций, были добавлены кастомные перемещения и обработка неучтенных инструкций.
Например, на ARMv8 инструкции с плавающей точкой являются расширением базового ISA и могут не поддерживаться всеми ARMv8 процами. Настройка процессора в Nyxstone включает его дефолтные расширения, позволяя ассемблировать и дизассемблировать любые инструкции расширений, такие как инструкции с плавающей точкой на ARMv8.
Для реверса машинного кода или определения точных байтов данной инструкции был реализован клиентский инструмент, облегчающий операции ассемблирования и дизассемблирования.
Указав нужную операцию с помощью флагов
Код: Скопировать в буфер обмена
Код: Скопировать в буфер обмена
Флаг
При ассемблировании инструкций, относительных счетчика программы, Nyxstone учитывает как позицию в коде, так и адреса предоставленных меток, обеспечивая корректные результаты.
По умолчанию Nyxstone использует архитектуру
Код: Скопировать в буфер обмена
Расширения архитектуры по умолчанию не включены в LLVM, так как предполагается, что реализован только базовый ISA. Поэтому, например, Nyxstone не может ассемблировать инструкцию с плавающей точкой на
Код: Скопировать в буфер обмена
Здесь пригодятся дополнительные опции конфигурации LLVM. Можно указать точную модель процессора, чтобы включить его стандартные функции, или активировать конкретные расширения ISA для заданной архитектуры набора инструкций. Для приведенной инструкции с плавающей точкой можно указать процессор как Cortex-M7 (который имеет расширение ARM для операций с плавающей точкой) или включить конкретную функцию для требуемого расширения.
Сначала укажем точную модель процессора:
Код: Скопировать в буфер обмена
Другой вариант - включить конкретное расширение ISA. Функции указываются через запятую, каждое значение предваряется плюсом (+) или минусом (-) в зависимости от того, надо ли включить или отключить функцию. В этом случае Nyxstone через LLVM сообщает о необходимой функции vfp2:
Код: Скопировать в буфер обмена
Также реализованы биндинги для Rust, так как этот язык обеспечивает безопасность работы с памятью и используется для создания технологии перезаписи бинарников Emproof Nyx.
Кроме того, для быстрого и простого использования Nyxstone многими людьми, были реализованы биндинги для Python.
Использование Nyxstone в качестве библиотеки обычно довольно просто. Основное требование для сборки и линковки Nyxstone - установленные на системе библиотеки LLVM версии 15, при этом соответствующий
Если указан путь к LLVM, Nyxstone будет искать
Дополнительные инструкции по сборке и требуемые пакеты документированы в GitHub [README].
Также возможно использовать Nyxstone без CMake; Makefile, который использовался до перехода на CMake, можно найти в репозитории.
Основные моменты использования C++ API Nyxstone, можно найти здесь.
Вот пример использования Nyxstone из Rust. В нем создается экземпляр Nyxstone. Он настроен на использование шестнадцатеричного представления для непосредственных значений в дизассемблированном коде и в остальном использует стандартную конфигурацию. Затем он используется для ассемблирования
Код: Скопировать в буфер обмена
Nyxstone опубликован как крейт на crates.io. Чтобы использовать Nyxstone в своем Rust-проекте, достаточно добавить его в
Вот пример использования Nyxstone из Python, где создается экземпляр Nyxstone с определенными настройками для CPU и стиля непосредственных значений. Затем демонстрируется, как ассемблировать и дизассемблировать некоторые инструкции с помощью этого экземпляра.
Python: Скопировать в буфер обмена
Nyxstone для Python публикуется через PyPI. Его можно установить из PyPI командой
В дорожной карте проекта можно ознакомиться с планируемыми функциями.
Переведено специально для XSS.is
Автор перевода: ordinaria1
Источник: www.emproof.com/introducing-nyxstone-an-llvm-based-disassembly-framework
Перезапись бинарников не зависит от компилятора и языка программирования, что даёт гибкость в модификации и хардении кода. Процесс включает три шага:
- Преобразование бинаря в промежуточное представление, которое не зависит от архитектуры.
- Трансформация этого представления
- Понижение обратно в машинный код и запись обратно в двоичный.
Изначально использовались Capstone для дизассемблирования и Keystone для ассемблирования, но эти фреймворки оказались не идеальны. Keystone часто выдавал некорректный машинный код и плохо сообщал об ошибках. Также в нём не имелось поддержки меток в ассемблере.
В результате был создан Nyxstone - библиотека для сборки и ревёрса на базе LLVM. После года тестирования на разных архитектурах, он стал открытым под MIT лицензией.
Nyxstone можно использовать как отдельный инструмент или как библиотеку для C++, Rust и Python.
Ключевые фичи Nyxstone:
- Основан на LLVM из-за мощных возможностей ассемблера и дизассемблера, использует бэкенд LLVM для генерации кода.
- Прямая линковка с LLVM обеспечивает компактность и легкость поддержки.
- Поддержка всех архитектур, совместимых с LLVM.
- Унифицированные возможности ассемблирования/дизассемблирования для простого перевода между машинным и ассемблерным кодом.
- Поддержка меток в ассемблере, включая определение произвольных меток и меток для инструкций. Полезно для ассемблирования условных и безусловных переходов, а также инструкций относительно счетчика программы.
- Гибкая и детальная конфигурация процессоров и их характеристик, позволяющая настраивать инструмент под конкретные расширения ISA и аппаратные характеристики.
Ключевые фичи объединенных возможностей (диз)ассемблирования Nyxstone:
- Интегрированные функции ассемблирования и дизассемблирования в едином фреймворке.
- Использование внутреннего ассемблера и дизассемблера LLVM для поддержки различных архитектур.
- Для дизассемблирования применяется API LLVM, конвертирующий машинный код в человеко-читаемый ассемблер.
- Функции ассемблирования доступны через внутренний бэкенд машинного кода LLVM.
- Nyxstone оборачивает внутренние объекты LLVM и хукает релевантные API, что позволяет:
- Бесшовно использовать функционал ассемблирования LLVM
- Добавлять дополнительные проверки ошибок без патчинга LLVM
- Использование диагностики LLVM для эффективного репорта проблем, включая точное место в ассемблерном коде, где возникла ошибка.
- Любая диагностика от LLVM трактуется как ошибка, что обеспечивает точный и полный отчёт ошибок для упрощения дебага и доработки кода.
Поддержка меток
Важная фича Nyxstone - поддержка меток в ассемблере. В отличие от других фреймворков (например, Keystone), можно определять метки, ссылающиеся на конкретные адреса. Это полезно при ассемблировании условных переходов и инструкций относительно счетчика программы.Еще один юзкейс - более гибкое размещение последовательностей инструкций. Пример - реализация теневого стека (для защиты адресов возврата от атак):
Код: Скопировать в буфер обмена
Код:
; addr 0x1000
add r10, 4 ; r10 holds ptr to the shadow stack
lea rbx, [rip + .ret] ; load the return address
mov [r10], rbx ; store the return address on the shadow stack
call some_fn ; call into the function
.ret:
; ...
; some functions in-between
; addr 0x1400
some_fn:
; ...
; addr 0x1458
mov rbx, [rsp] ; load return address
cmp [r10], rbx ; ensure return address is unchanged
jne .return_addr_compromised ; at 0x1800
sub r10, 4 ; clean up shadow stack
ret
Nyxstone может ассемблировать такие снипеты отдельно, если указать адреса отсутствующих меток. Для патча на 0x1000 нужен адрес
some_fn
, а для ассемблирования на 0x1458 - адрес .return_addr_compromised
.Для этого юзкейса важно, чтобы сгенеренный машинный код корректно переходил по пользовательским меткам и ссылался на них. Иначе вставленные в бинарник инструкции могут уйти не туда, что приведет к неправильному потоку выполнения или инвалидным обращениям к памяти.
При разработке Nyxstone обнаружилось, что в некоторых случаях LLVM генерит код, который ссылается на адрес
0
вместо адреса, указанного в метке.Чтобы Nyxstone не выдавал неверные байты инструкций, были добавлены кастомные перемещения и обработка неучтенных инструкций.
Опции конфигурации
Использование LLVM в качестве основы для Nyxstone даёт преимущество прямого доступа к опциям конфигурации архитектуры LLVM. От этого можно настраивать Nyxstone под конкретные процессоры и включать всякие расширения архитектуры через фишки процессора.Например, на ARMv8 инструкции с плавающей точкой являются расширением базового ISA и могут не поддерживаться всеми ARMv8 процами. Настройка процессора в Nyxstone включает его дефолтные расширения, позволяя ассемблировать и дизассемблировать любые инструкции расширений, такие как инструкции с плавающей точкой на ARMv8.
Примеры использования
Рассмотрим, как применять основные функции Nyxstone на практике. Сначала продемонстрируем их в клиентском инструменте (CLI), а затем посмотрим, как интегрировать Nyxstone в качестве библиотеки в разных ЯП.Для реверса машинного кода или определения точных байтов данной инструкции был реализован клиентский инструмент, облегчающий операции ассемблирования и дизассемблирования.
Указав нужную операцию с помощью флагов
-A/--assemble
и -D/--disassemble
, можно легко переводить инструкции между машинным кодом и языком ассемблера:Код: Скопировать в буфер обмена
Код:
$ ./nyxstone -A "xor rax, rbx"
# 0x00000000: xor rax, rbx - [ 48 31 d8 ]
$ ./nyxstone -D "13 37"
# 0x00000000: adc esi, dword ptr [rdi] - [ 13 37 ]
$./nyxstone -A "xor rax, rbx; add rax, 10"
# 0x00000000: xor rax, rbx - [ 48 31 d8 ]
# 0x00000003: add rax, 10 - [ 48 83 c0 0a ]
Адреса и метки
CLI Nyxstone позволяет задавать стартовый адрес для ассемблирования и определять метки. Используя предыдущий пример, можно ассемблировать два фрагмента кода отдельно:Код: Скопировать в буфер обмена
Код:
$ ./nyxstone --address "0x1000" -A
"
add r10, 4
lea rbx, [rip + .ret]
mov [r10], rbx
call some_fn
.ret:
" --labels "some_fn=0x1400"
# 0x00001000: add r10, 4 - [ 49 83 c2 04 ]
# 0x00001004: lea rbx, [rip + .ret] - [ 48 8d 1d 08 00 00 00 ]
# 0x0000100b: mov qword ptr [r10], rbx - [ 49 89 1a ]
# 0x0000100e: call some_fn - [ e8 ed 03 00 00 ]
$ ./nyxstone --address "0x1458" -A
"
mov rbx, [rsp + 4]
cmp rbx, [r10]
jne .return_addr_compromised
sub r10, 4
ret
" --labels ".return_addr_compromised=0x1800"
# 0x00001458: mov rbx, qword ptr [rsp + 4] - [ 48 8b 5c 24 04 ]
# 0x0000145d: cmp rbx, qword ptr [r10] - [ 49 3b 1a ]
# 0x00001460: jne .return_addr_compromised - [ 0f 85 9a 03 00 00 ]
# 0x00001466: sub r10, 4 - [ 49 83 ea 04 ]
# 0x0000146a: ret - [ c3 ]
Флаг
--address
задает стартовый адрес для ассемблирования, что видно по адресу первой инструкции в выводе Nyxstone. С помощью флага --labels
можно определять произвольные метки. Несколько меток задаются как пары "ключ-значение", разделенные запятыми, например: --labels "label0=0x1000,label1=0x1200"
.При ассемблировании инструкций, относительных счетчика программы, Nyxstone учитывает как позицию в коде, так и адреса предоставленных меток, обеспечивая корректные результаты.
Архитектура и конфигурация
Nyxstone предоставляет гибкие опции для настройки под разные системы, показывая возможности конфигурации архитектуры и специфичных для ISA расширений.По умолчанию Nyxstone использует архитектуру
x86_64
, но может быть настроен на любую архитектуру, поддерживаемую связанной библиотекой LLVM, через флаг --arch
. Входными данными может быть либо LLVM triple для архитектуры, либо ее сокращение. Это позволяет ассемблировать инструкции для различных архитектур, таких как armv8m
, armv8-thumb
, aarch64
, riscv32
и других:Код: Скопировать в буфер обмена
Код:
$ ./nyxstone --arch "armv8m" -A "add r0, r1"
# 0x00000000: add r0, r0, r1 - [ 01 00 80 e0 ]
$ ./nyxstone --arch "thumb8" -A "add r0, r1"
# 0x00000000: add r0, r1 - [ 08 44 ]
$ ./nyxstone --arch "aarch64" -A "add w0, w1, w2"
# 0x00000000: add w0, w1, w2 - [ 20 00 02 0b ]
$ ./nyxstone --arch "riscv32" -A "add t0, t1, zero"
# 0x00000000: add t0, t1, zero - [ b3 02 03 00 ]
Расширения архитектуры по умолчанию не включены в LLVM, так как предполагается, что реализован только базовый ISA. Поэтому, например, Nyxstone не может ассемблировать инструкцию с плавающей точкой на
armv8m
в базовой конфигурации:Код: Скопировать в буфер обмена
Код:
$ ./nyxstone --arch "armv8m" -A "vadd.f32 s0, s1, s2"
# Could not assemble vadd.f32 s0, s0, s1 (= Error during assembly: error: instruction requires: VFP2
# vadd.f32 s0, s0, s1
# ^
# )
Здесь пригодятся дополнительные опции конфигурации LLVM. Можно указать точную модель процессора, чтобы включить его стандартные функции, или активировать конкретные расширения ISA для заданной архитектуры набора инструкций. Для приведенной инструкции с плавающей точкой можно указать процессор как Cortex-M7 (который имеет расширение ARM для операций с плавающей точкой) или включить конкретную функцию для требуемого расширения.
Сначала укажем точную модель процессора:
Код: Скопировать в буфер обмена
Код:
$ ./nyxstone --arch "armv8m" --cpu "cortex-m7" -A "vadd.f32 s0, s1, s2"
# 0x00000000: vadd.f32 s0, s0, s1 - [ 30 ee 20 0a ]
Другой вариант - включить конкретное расширение ISA. Функции указываются через запятую, каждое значение предваряется плюсом (+) или минусом (-) в зависимости от того, надо ли включить или отключить функцию. В этом случае Nyxstone через LLVM сообщает о необходимой функции vfp2:
Код: Скопировать в буфер обмена
Код:
$ ./nyxstone --arch "armv8m" --features "+vfp2" -A "vadd.f32 s0, s0, s1"
# 0x00000000: vadd.f32 s0, s0, s1 - [ 20 0a 30 ee ]
Nyxstone в качестве библиотеки
Хоть и CLI - отличный инструмент для повседневного ассемблирования или дизассемблирования инструкций, Nyxstone в своей основе является библиотекой. Nyxstone написан на C++ для взаимодействия с LLVM и, соответственно, имеет C++ API.Также реализованы биндинги для Rust, так как этот язык обеспечивает безопасность работы с памятью и используется для создания технологии перезаписи бинарников Emproof Nyx.
Кроме того, для быстрого и простого использования Nyxstone многими людьми, были реализованы биндинги для Python.
Использование Nyxstone в качестве библиотеки обычно довольно просто. Основное требование для сборки и линковки Nyxstone - установленные на системе библиотеки LLVM версии 15, при этом соответствующий
llvm-config
должен быть либо в пути, либо нужно указать расположение библиотек LLVM через переменную окружения NYXSTONE_LLVM_PREFIX
.Если указан путь к LLVM, Nyxstone будет искать
llvm-config
по пути $NYXSTONE_LLVM_PREFIX/bin/llvm-config
.Дополнительные инструкции по сборке и требуемые пакеты документированы в GitHub [README].
C++ использование
Прямой способ взаимодействия с Nyxstone - через C++. Для линковки Nyxstone и сборки CLI в качестве примера кода для C++ API используется CMake. Пример интеграции Nyxstone в проект с помощью CMake приведен в README Nyxstone.Также возможно использовать Nyxstone без CMake; Makefile, который использовался до перехода на CMake, можно найти в репозитории.
Основные моменты использования C++ API Nyxstone, можно найти здесь.
Использование в Rust
Биндинги Nyxstone для Rust являются приоритетными. Цель для биндингов Rust и Python - соответствие особенностям каждого языка, используя их специфические возможности.Вот пример использования Nyxstone из Rust. В нем создается экземпляр Nyxstone. Он настроен на использование шестнадцатеричного представления для непосредственных значений в дизассемблированном коде и в остальном использует стандартную конфигурацию. Затем он используется для ассемблирования
jne .label
в объект Instruction
, который содержит не только машинный код, но и адрес, и ассемблерное представление каждой инструкции. Наконец, он используется для дизассемблирования байтов инструкции 0x31 0xd8
в читаемую инструкцию.Код: Скопировать в буфер обмена
Код:
use std::collections::HashMap;
use anyhow::Result;
use nyxstone::{IntegerBase, Nyxstone, NyxstoneConfig, Instruction};
fn main() -> Result<()> {
let nyxstone = Nyxstone::new(
"x86_64",
NyxstoneConfig {
immediate_style: IntegerBase::HexPrefix,
..Default::default()
},
)?;
// Можно также ассемблировать в [`u8`], используя `assemble()`
let instructions = nyxstone.assemble_to_instructions(
"jne .label",
0x1000,
&HashMap::from([(".label", 0x1200)]),
)?;
assert_eq!(
instructions,
vec![Instruction {
address: 0x1000,
assembly: "jne .label",
bytes: vec![0x0f, 0x85, 0xfa, 0x01, 0x00, 0x00]
}]
);
// Можно также дизассемблировать в объекты [`Instruction`], используя `disassemble_to_instructions()`
let disassembly = nyxstone.disassemble(
&[0x31, 0xd8],
/* address= */ 0x0,
// Количество инструкций для дизассемблирования (0 = все)
/* count = */ 0,
)?;
assert_eq!(disassembly, "xor eax, ebx\n".to_owned());
Ok(())
}
Nyxstone опубликован как крейт на crates.io. Чтобы использовать Nyxstone в своем Rust-проекте, достаточно добавить его в
Cargo.toml
. Но надо убедиться, что LLVM 15 установлен и llvm-config
доступен через переменную окружения или в системном пути; иначе Nyxstone не сможет корректно слинковаться.Использование в Python
Для удобства экспериментов также предоставляются биндинги для Python с использованиемpybind11
.Вот пример использования Nyxstone из Python, где создается экземпляр Nyxstone с определенными настройками для CPU и стиля непосредственных значений. Затем демонстрируется, как ассемблировать и дизассемблировать некоторые инструкции с помощью этого экземпляра.
Python: Скопировать в буфер обмена
Код:
from nyxstone import Nyxstone, IntegerBase, Instruction
# Создание экземпляра Nyxstone для x86_64 с использованием CPU corei7 и шестнадцатеричным стилем вывода непосредственных значений
nyxstone = Nyxstone("x86_64", cpu="corei7", immediate_style=IntegerBase.HexPrefix)
# Ассемблирование `xor rax, rax` в байты, представленные списком целых чисел
nyxstone.assemble("xor rax, rax")
# = [0x48, 0x31, 0xc0]
# Ассемблирование `jmp label` в информацию об инструкции, содержащую адрес, ассемблерный код и байты машинного кода
nyxstone.assemble_to_instructions("jmp label", address=0x1000, labels={"label": 0x1080})
# = [Instruction(address=0x1000, assembly="jmp label", bytes=[0xeb, 0x7e])]
# Дизассемблирование `0x13 0x37` в строку
nyxstone.disassemble([0x13, 0x37])
# 'adc esi, dword ptr [rdi]\n'
Nyxstone для Python публикуется через PyPI. Его можно установить из PyPI командой
pip install nyxstone
или из исходников, используя pip install .
в поддиректории с python-биндингами.Заключение
В этой статье был представлен открытый фреймворк для ассемблирования и дизассемблирования Nyxstone, построенный на базе LLVM. Он предлагает широкую поддержку конфигурации архитектур и их возможностей, точное сообщение об ошибках и способность определять произвольные метки при ассемблировании. Nyxstone доступен через API для C++, Rust и Python, а также включает универсальный инструмент командной строки.В дорожной карте проекта можно ознакомиться с планируемыми функциями.
Переведено специально для XSS.is
Автор перевода: ordinaria1
Источник: www.emproof.com/introducing-nyxstone-an-llvm-based-disassembly-framework