HOME FORUMS MEMBERS RECENT POSTS LOG IN  
× Авторизация
Имя пользователя:
Пароль:
Нет аккаунта? Регистрация
Баннер 1   Баннер 2
НОВЫЕ ТОРГОВАЯ НОВОСТИ ЧАТ
loading...
Скрыть
Вернуться   ANTICHAT > ПРОГРАММИРОВАНИЕ > С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

  #1  
Старый 10.07.2024, 13:57
Receiver
Постоянный
Регистрация: 26.03.2016
Сообщений: 660
С нами: 5332594

Репутация: 163


По умолчанию

Приветствую вас, дорогие читатели! В этой статье мы погрузимся в изучение интересного и важного аспекта защиты программного обеспечения. Конкретно будем рассматривать принцип работы меток протекторов, таких как VMProtect, Themida и других. Я стараюсь сделать это максимально просто и понятно, так что давайте приступим.

Метки играют ключевую роль в работе протекторов и без них просто не обойтись. Они необходимы для обозначения блоков кода, которые подлежат защите, будь то виртуализация, мутация, шифрование или сжатие. Метки не только облегчают протектору поиск нужного места в коде, но также обеспечивают code cave для прыжка и задают границы блока для защиты.

Существует два распространенных типа меток: inline assembly marker и library call marker. Первый тип, inline assembly marker, является самым удобным, так как он позволяет вставить инструкции любого размера непосредственно в тело функции на этапе компиляции. Это означает, что простым протекторам не нужно будет выделять память под защитный код, который оборачивает ваш блок кода.

Однако, в настоящее время самым популярным способом является использование library call marker. Это связано с тем, что компилятор Microsoft не поддерживает inline assembly в X64. Кроме того, этот способ позволяет быть уверенным на 10 миллиардов процентов, что это именно метка протектора, а не простое совпадение паттерна в случайном месте кода.

Так давайте же посмотрим как выглядят метки разных протекторов в листинге:





Рисунок 1 — Метки Themida в листинге.



Рисунок 2 — Метки VMProtect в листинге.

На этих скриншотах видно как происходит вызов функций внешней динамической библиотеки и это действительно правда, ведь в импортах у нас появилась библиотека SDK VMProtect + 2 этих вызываемых функции.



Рисунок 3 — Метки VMProtect в таблице импортов.

Теперь на основе полученных знаний попробуем реализовать собственные метки. Будем пользоваться способом library call marker. Для начала нужно сделать 2 проекта: Marker-SDK представляет из себя динамическую библиотеку с функциями заглушками, Marker-APP занимется поиском меток нашего Marker-SDK в PE-файле и их реализацией. Именно динамическая библиотека нам необходима потому что она может на 10 миллиардов процентов гарантировать внешний вызов функции в коде и давать нам запись в таблице импорта, в том время как статическая вполне может заинлайнить код своей функции внутрь нашей.

Первым этапом будет описывание нашего Marker-SDK, для этого создадим sdk.h и вставим туда следующий код:


C++:





Код:
#ifndef MARKER_SDK_H
#define MARKER_SDK_H
#ifdef MARKER_SDK_EXPORTS
#define MARKER_API __declspec(dllexport)
#else
#define MARKER_API __declspec(dllimport)
#endif
// MARKER_SDK_EXPORTS
extern
"C"
{
MARKER_API
void
begin_encrypted
(
void
)
;
MARKER_API
void
end_encrypted
(
void
)
;
}
#endif
// MARKER_SDK_H


Так же для корректной компиляции нам понадобиться файл заглушка sdk.cpp, в котором будет храниться пустая имплементация этих 2-х функций.

Следующим этапом у нас идёт реализация Marker-APP. Тут нам понадобиться какой-нибудь PELib, PEBliss, либо же можно просто использовать структуры Windows, но я буду пользоваться кроссплатформенным linux-pe. Весь код выкладывать нет смысла, так что рассмотрим исключительно функцию поиска метки:


C++:





Код:
auto
find_sdk_imports
(
std
::
error_code
&
ec
)
{
auto
result
=
std
::
pair

{
}
;
// Получаем директории таблицы импортов
auto
imports_boundary
=
image_
->
get_directory
(
win
::
directory_entry_import
)
;
if
(
!
imports_boundary
)
{
ec
=
std
::
make_error_code
(
std
::
errc
::
no_such_file_or_directory
)
;
return
result
;
}
// Перебираем все импортируемые библиотеки
for
(
auto
library
=
reinterpret_cast

(
image_
->
rva_to_ptr
(
imports_boundary
->
rva
)
)
;
library
->
rva_name
;
++
library
)
{
// Получаем имя библиотеки
auto
library_name
=
std
::
string
{
reinterpret_cast

(
image_
->
rva_to_ptr
(
library
->
rva_name
)
)
}
;
if
(
library_name
!=
"Marker-SDK.dll"
)
continue
;
// Перебираем все импортируемые функции для библиотеки
for
(
auto
function
=
reinterpret_cast

(
image_
->
rva_to_ptr
(
library
->
rva_first_thunk
)
)
;
function
->
address
;
++
function
)
{
// Получаем имя функции
auto
function_name
=
std
::
string
{
reinterpret_cast

(
image_
->
rva_to_ptr
(
function
->
address
)
)
->
name
}
;
// Получаем смещение до функции в IAT
// Так же я конвертирую в смещение относительно начала буффера
auto
iat_address
=
image_
->
ptr_to_raw
(
function
)
;
if
(
function_name
==
"begin_encrypted"
)
{
result
.
first
=
iat_address
;
}
else
if
(
function_name
==
"end_encrypted"
)
{
result
.
second
=
iat_address
;
}
}
}
return
result
;
}


На выходе эта функция вернёт нам пару [начало, конец] адресов меток на IAT. Теперь мы можем подключить наш Marker-SDK к любому проекту и чтобы заменить их на полезную нагрузку остаётся прогнать PE-файл любым дизассемблером и сравнить расчитанные адреса для call/jmp с адресами меток.

Текст статьи написан с помощью mistral.ai
 
Ответить с цитированием

  #2  
Старый 10.07.2024, 17:14
Madjestik
Участник форума
Регистрация: 17.08.2017
Сообщений: 145
С нами: 4599350

Репутация: 98
По умолчанию

Тема каеееф, спасибо тебе за обучение!
 
Ответить с цитированием
Ответ





Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT ™ © 2001- Antichat Kft.