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

  #1  
Старый 23.02.2023, 00:19
0Z0SK0
Познающий
Регистрация: 14.02.2023
Сообщений: 41
С нами: 1709244

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

Lazy_importer

C++ (header only) библиотека для сокрытия импорта системных (и не только) DLL и вызовов из них.

Введение:

Для начала необходимо ответить на вопрос: Зачем скрывать импорты DLL и(или) вызовы из них?

Простой ответ: чтобы усложнить жизнь ревёрсеру ваших программ.

Более раскрытый ответ: чтобы запутать ревёрсера во время "вскрытия" вашей программы, лишив его информации об используемых системных (и не только) библиотеках, о вызывах из них. Данная информация играет одну из ключевых ролей в ревёрсе программы, примеры её использования различны, начиная от банальных хуков функций внутри сторонних длл, заканчивая использованием дыр в этих же библиотеках (например пустые пространства кода внутри сегментов длл, для последующего встраивания туда шелл-кода).


Преимущества:
  • Не выделяет виртуальную память для какой-либо функции (или при загрузке DLL).
  • В исполняемом виде представляет из себя достаточно простой asm-код.
  • Не оставляет defined strings при импорте/использовании функций библиотеки.
  • Легковстраиваемая в любой проект (представляет из себя одиночный hpp файл).
  • Интуитивно понятные названия inlined-функций.

Использование:

Вызов любой из функций осуществляется при помощи скрытого метода LI_FN, он динамичен, принимает аргументы и возвращает значения оригинальной функции.

Принцип работы заключается в извлечении оригинальной функции из библиотеки, с последующим встраиванием функции в непосредственный код программы, предварительно подвергнув её hash-morping`у. При традиционном использовании, функция импортируется из определенной библиотеки (указанной в PE-заголовках) и не находится напрямую в исполняемом коде.


C++:





Код:
// Использование скрытым методом, не отображается в разделе импортов PE-заголовков.
LI_FN
(
OutputDebugStringA
)
(
"string"
)
;
// Оригинальное использование, отобразится в любом дебаггере при изучении PE-заголовков,
// в разделе импорты.
OutputDebugStringA
(
"string"
)
;


Лучше всего это демонстрируется при reverse-engineering`е исполняемого кода.

При традиционном способе всё отлично проявляется.

1677095224124.png0Z0SK0 · 22 Фев 2023 в 23:19' data-fancybox="lb-post-1261067" data-lb-caption-extra-html="" data-lb-sidebar-href="" data-single-image="1" data-src="https://www.blast.hk/attachments/190991/" style="cursor: pointer;" title="1677095224124.png">


При использовании LI_FN, импортированная функция никак не отображается, и при исполнении представляет из себя примерно подобный код

Код:





Код:
for ( i = *(_QWORD **)(*(_QWORD *)(__readgsqword(0x60u) + 24) + 16i64); ; i = (_QWORD *)*i )
  {
    v1 = i[6];
    v2 = *(unsigned int *)(*(signed int *)(v1 + 60) + v1 + 136);
    v3 = (_DWORD *)(v2 + v1);
    if ( v2 + v1 != v1 )
    {
      LODWORD(v4) = v3[6];
      if ( (_DWORD)v4 )
        break;
    }
LABEL_8:
    ;
  }
  while ( 1 )
  {
    v4 = (unsigned int)(v4 - 1);
    v5 = -2128831035;
    v6 = (char *)(v1 + *(unsigned int *)((unsigned int)v3[8] + 4 * v4 + v1));
    v7 = *v6;
    v8 = (signed __int64)(v6 + 1);
    if ( v7 )
    {
      do
      {
        ++v8;
        v5 = 16777619 * (v5 ^ v7);
        v7 = *(_BYTE *)(v8 - 1);
      }
      while ( v7 );
      if ( v5 == -973690651 )
        break;
    }
    if ( !(_DWORD)v4 )
      goto LABEL_8;
  }
  ((void (__fastcall *)(const char *))(v1
                                     + *(unsigned int *)(v1
                                                       + (unsigned int)v3[7]
                                                       + 4i64 * *(unsigned __int16 *)(v1 + (unsigned int)v3[9] + 2 * v4))))("hello world");


LI_FNпомимо обычного использования, позволяет различными способами оптимизировать ваш код и имеет для этого определенные суб-параметры.

C++:





Код:
// cached параметр вычисляет результат выполнения при первом использовании,
// и в дальнейшем возвращает сохраненное значение.
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
cached
(
)
// safe параметр позволяет безопасно использовать вызовы функций без unhandled exception,
// т.к при неверном выполнении функции, она попросту возвратит значение 0, не вызывая краш.
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
safe
(
)
// in параметр в отличие от других, позволяет принудительно указать источник функции,
// таким источник может выступать предварительно загруженный модуль.
// В данном примере, функция вызывается непосредственно из библиотеки Kernel32.DLL,
// тогда как без использования параметра in, программа попытается найти требуемую
// функцию в уже загруженных иных библиотеках.
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
in
(
"Kernel32.dll"
)
// Остальные параметры представляют из себя комбинацию описанных выше
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
safe_cached
(
)
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
in_cached
(
"Kernel32.dll"
)
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
in_safe
(
"Kernel32.dll"
)
LI_FN
(
OutputDebugStringA
)
(
"string"
)
.
in_safe_cached
(
"Kernel32.dll"
)


Но, если функция не найдена ни в одной из импортированных библиотек, что в этом случае?

Параметр in в таком случае так-же не поможет, вам необходимо предварительно загрузить требуемые библиотеки при помощи стандартных способов.

Если же не загрузить их, ваш исполняемый код вызовет краш и программа завершится.

C++:





Код:
// Предварительно загружаем требуемую нам библиотеки скрытым способом
LI_FN
(
"LoadLibraryA"
)
(
"Kernel32.DLL"
)
;
// Теперь, мы можем использовать любую из функций внутри этой библиотеки
LI_FN
(
"OutputDebugStringA"
)
(
"string"
)
.
in
(
LI_MODULE
(
"Kernel32.DLL"
)
.
get
(
)
)


При использовании LI_FN важно помнить о дефайнах/макросах функций.

Например OutputDebugString является обёрткой для двух различных функций - OutputDebugStringA и OutputDebugStringW в зависимости от используемой многобайтовой или иной кодировки.

При вызове дефайна OutputDebugString напрямую, ваш исполняемый код так-же вызовет краш.

В таком случае необходимо вызывать функцию напрямую, избегая различные обёртки.

C++:





Код:
// crash!!!
LI_FN
(
OutputDebugString
)
(
"string"
)
// всё ok!
LI_FN
(
OutputDebugStringA
)
(
"string"
)
// всё ok!
LI_FN
(
OutputDebugStringW
)
(
L
"string"
)


Не стоит забывать и о APISET-библиотеках, такой является Ole32.DLL. В данном случае необходимо вызывать функции из библиотеки combase.dll.

C++:





Код:
// crash!!!
// apiset библиотека
LI_FN
(
LoadLibraryA
)
(
"Ole32.DLL"
)
;
LI_FN
(
CoCreateGuid
)
.
in
(
LI_MODULE
(
"Ole32.DLL"
)
.
get
(
)
)
(
&
guid
)
;
// всё ok!
LI_FN
(
LoadLibraryA
)
(
"combase.dll"
)
;
LI_FN
(
CoCreateGuid
)
.
in
(
LI_MODULE
(
"combase.dll"
)
.
get
(
)
)
(
&
guid
)
;


Предирективы:

Lazy_Importer позволяет указать различные дефайны для эффективного и правильного использования своих фунций.

C++:





Код:
// позволяет импортировать библиотеки вне зависимости от регистра в их названии,
// без использования данной предирективы, следует соблюдать регистр, иначе последует краш.
#define LAZY_IMPORTER_CASE_INSENSITIVE
// заменяет get() на параметр safe()
#define LAZY_IMPORTER_CACHE_OPERATOR_PARENS
// добавляет более жесткие ограничения при поиске функции из импортированных библиотек.
#define LAZY_IMPORTER_HARDENED_MODULE_CHECKS


Библиотека: https://github.com/JustasMasiulis/lazy_importer

Автор этой блестящей библиотеки: JustasMasiulis и сообщество GitHub

Цитата:
Сообщение от Спойлер  


C++:





Код:
// Загружаем библиотеку libcurl.dll
const
char
*
libraryPath
=
(
Core
::
getCurrentDirectory
(
)
+
XorStr
(
"\\libcurl.dll"
)
)
.
c_str
(
)
;
HMODULE library
=
LI_FN
(
LoadLibraryA
)
(
libraryPath
)
;
FILE
*
fp
;
long
httpStatusCode
=
0
;
CURL
*
curl
;
// Инициализация CURL.
curl
=
LI_FN
(
curl_easy_init
)
.
get
(
)
(
)
;
// Если CURL готов к работе.
if
(
curl
)
{
setDefaultCURLOptions
(
curl
)
;
// Открываем файл вывода.
fp
=
fopen
(
out
,
XorStr
(
"wb"
)
)
;
LI_FN
(
curl_easy_setopt
)
.
get
(
)
(
curl
,
CURLOPT_URL
,
url
)
;
LI_FN
(
curl_easy_setopt
)
.
get
(
)
(
curl
,
CURLOPT_WRITEFUNCTION
,
Core
::
curlWriteToFile
)
;
LI_FN
(
curl_easy_setopt
)
.
get
(
)
(
curl
,
CURLOPT_WRITEDATA
,
fp
)
;
CURLcode result
=
LI_FN
(
curl_easy_perform
)
.
get
(
)
(
curl
)
;
LI_FN
(
curl_easy_getinfo
)
.
get
(
)
(
curl
,
CURLINFO_RESPONSE_CODE
,
&
httpStatusCode
)
;
if
(
result
==
CURLE_OK
&&
httpStatusCode
==
200
)
{
// Процедуры очистки и закрытия файла.
LI_FN
(
curl_easy_cleanup
)
.
get
(
)
(
curl
)
;
fclose
(
fp
)
;
return
true
;
}
else
{
Network
::
lastError
=
httpStatusCode
;
LI_FN
(
curl_easy_cleanup
)
.
get
(
)
(
curl
)
;
fclose
(
fp
)
;
return
false
;
}
}
LI_FN
(
curl_easy_cleanup
)
.
get
(
)
(
curl
)
;


 
Ответить с цитированием

  #2  
Старый 23.02.2023, 00:40
AeSiK256
Познающий
Регистрация: 07.01.2023
Сообщений: 56
С нами: 1763923

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

опять небезопасный способ!

да ты надоел
 
Ответить с цитированием

  #3  
Старый 23.02.2023, 00:41
0Z0SK0
Познающий
Регистрация: 14.02.2023
Сообщений: 41
С нами: 1709244

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

Цитата:
Сообщение от AeSiK256  

опять небезопасный способ!
да ты надоел
Укажи где именно.
 
Ответить с цитированием

  #4  
Старый 23.02.2023, 00:44
AeSiK256
Познающий
Регистрация: 07.01.2023
Сообщений: 56
С нами: 1763923

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

Цитата:
Сообщение от 0Z0SK0  

Укажи где именно.
рантайм дебаг = F
 
Ответить с цитированием

  #5  
Старый 23.02.2023, 00:45
0Z0SK0
Познающий
Регистрация: 14.02.2023
Сообщений: 41
С нами: 1709244

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

Цитата:
Сообщение от AeSiK256  

рантайм дебаг = F
Не совсем понимаю о чем ты, и как это относится к теме.
 
Ответить с цитированием

  #6  
Старый 23.02.2023, 03:53
EclipsedFlow
Познавший АНТИЧАТ
Регистрация: 18.09.2017
Сообщений: 1,044
С нами: 4553429

Репутация: 153


По умолчанию

Цитата:
Сообщение от 0Z0SK0  

Не совсем понимаю о чем ты, и как это относится к теме.
Я так понимаю что если у тебя какая-то ошибка выскочит или краш на пустом месте и тебе будет нужно искать причину чтобы это исправить. Ты попросту её не найдешь так как runtime-debug -> press F или это будет очень сложно сделать.
 
Ответить с цитированием

  #7  
Старый 23.02.2023, 04:07
0Z0SK0
Познающий
Регистрация: 14.02.2023
Сообщений: 41
С нами: 1709244

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

Цитата:
Сообщение от Scar_  

Я так понимаю что если у тебя какая-то ошибка выскочит или краш на пустом месте и тебе будет нужно искать причину чтобы это исправить. Ты попросту её не найдешь так как runtime-debug -> press F или это будет очень сложно сделать.
Для этого предусмотрены методы safe и nt_safe для LI_FN вызова, и тоже самое для LI_MODULE.

Возвращающие 0 при неправильном выполнении функции, минуя появление undefined behavior.

Цитата:
Сообщение от Спойлер  


https://github.com/JustasMasiulis/lazy_importer/blob/master/include/lazy_importer.hpp:





Код:
struct
safe_module_enumerator
{
using
value_type
=
const
detail
::
win
::
LDR_DATA_TABLE_ENTRY_T
;
value_type
*
value
;
value_type
*
head
;
LAZY_IMPORTER_FORCEINLINE
safe_module_enumerator
(
)
noexcept
:
safe_module_enumerator
(
ldr_data_entry
(
)
)
{
}
LAZY_IMPORTER_FORCEINLINE
safe_module_enumerator
(
const
detail
::
win
::
LDR_DATA_TABLE_ENTRY_T
*
ldr
)
noexcept
:
value
(
ldr
->
load_order_next
(
)
)
,
head
(
value
)
{
}
LAZY_IMPORTER_FORCEINLINE
void
reset
(
)
noexcept
{
value
=
head
->
load_order_next
(
)
;
}
LAZY_IMPORTER_FORCEINLINE
bool
next
(
)
noexcept
{
value
=
value
->
load_order_next
(
)
;
return
value
!=
head
&&
value
->
DllBase
;
}
}
;


 
Ответить с цитированием

  #8  
Старый 24.02.2023, 23:57
colby57
Новичок
Регистрация: 13.02.2022
Сообщений: 12
С нами: 2236305

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

Цитата:
Сообщение от 0Z0SK0  

Более раскрытый ответ: чтобы запутать ревёрсера во время "вскрытия" вашей программы, лишив его информации об используемых системных (и не только) библиотеках, о вызывах из них.
Увы, но реверсеру данная библиотека ничем не мешает, более того, она оставляет важный паттерн за собой из-за которого можно отследить все дальнейшие вызовы, а именно - обращение к PEB.

Более подробно как находить все вызовы импортов от LazyImport можно прочитать у меня в статье -> https://yougame.biz/threads/247347/

Хорошим аналогом для этой библиотеки может послужить любой коммерческий протектор, тот же VMProtect или Code Virtualizer с лёгкой вм, которые не будут так открыто палить ваш вызов
 
Ответить с цитированием
Ответ





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


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




ANTICHAT ™ © 2001- Antichat Kft.