Показать сообщение отдельно

  #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
)
;


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