Blog. Just Blog

Получение списка DLL, загруженных в текущий процесс

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Сегодня я покажу вам небольшой трюк, с помощью которого вы сможете получить список всех DLL (динамических библиотек), загруженных в ваше приложение. Хитрость заключается в том, что для этого мы вообще не будем использовать никакие API.

Сперва немного теории. Каждому процессу в системе соответствует особая структура PEB (Process Enviroment Block), в ней содержится большое количество полезной информации о процессе. Адрес PEB можно узнать из другой структуры - TIB (Thread Information Block), адрес которой, в свою очередь, всегда можно получить по фиксированному адресу [FS:0]. Так вот, одно из полей PEB указывает на массив структур, в которых содержится информация о всех динамических библиотеках, загруженных в адресное пространство процесса. Этот массив называется PEB_LDR_DATA. Перебирая по очереди записи из этого массива, можно получить интересующий нас список DLL, а также адреса загрузки в память и Virtual Address точки входа этих библиотек.

Переходим к программированию. Flat Assembler "из коробки" не знает структур для работы с PEB_LDR_DATA, поэтому придется ему в этом помочь:
  1. struct UNICODE_STRING
  2.   Length        dw ?
  3.   MaximumLength dw ?
  4.   Buffer        dd ?
  5. ends
  6.  
  7. struct LIST_ENTRY
  8.   Flink         dd ?
  9.   Blink         dd ?
  10. ends
  11.  
  12. struct LDR_DATA_ENTRY
  13.   InMemoryOrderModuleList LIST_ENTRY
  14.   BaseAddress   dd ?
  15.   EntryPoint    dd ?
  16.   SizeOfImage   dd ?
  17.   FullDllName   UNICODE_STRING
  18.   BaseDllName   UNICODE_STRING
  19.   Flags         dd ?
  20.   LoadCount     dw ?
  21.   TlsIndex      dw ?
  22.   HashTableEntry LIST_ENTRY
  23.   TimeDateStamp dd ?
  24. ends
Записи перелинкованы между собой посредством поля InMemoryOrderModuleList.Flink, в котором прописана информация об адресе следующей записи. Важно учитывать, что массив "закольцован", то есть последняя запись ссылается на первую. При переборе надо проверять, например, поле BaseAddress текущей записи, если оно нулевое, то обработку следует остановить. Записи в массиве располагаются в порядке их загрузки в память.

Сам код перебора простейший. Для удобства я оставил только сам алгоритм, полезную нагрузку вы можете добавить по своему усмотрению.
  1.         ; EAX -> PEB
  2.         mov     eax,[fs:30h]
  3.         ; EAX -> PEB_LDR_DATA
  4.         mov     eax,[eax+0Ch]
  5.         ; EBX -> InInitializationOrderModuleList
  6.         mov     ebx,[eax+1Ch]
  7. @@:
  8.         ; Последняя запись?
  9.         cmp     [ebx+LDR_DATA_ENTRY.BaseAddress],0
  10.         ; Остановить обработку
  11.         je      @f
  12.  
  13.         ...
  14.         ...
  15.         ; EBX -> LDR_DATA_ENTRY
  16.         ; Выполнить нужные действия
  17.         ...
  18.         ...
  19.  
  20.         ; Указатель на следующую запись
  21.         mov     ebx,[ebx+LDR_DATA_ENTRY.InMemoryOrderModuleList.Flink]
  22.         jmp     @b
  23. @@:
Так же не забывайте, что все строки в структурах хранятся в юникоде. При необходимости конвертируйте их в нужный формат.

Ну и, напоследок, ответ на традиционный вопрос "зачем это нужно?". Например, таким способом защитный модуль программы может контролировать, что все используемые системные библиотеки загружены из системной директории. Для взлома некоторых приложений используются так называемые прокси-dll, которые имеют имя одной из системных библиотек, импортируемых приложением, но загружаются из папки с программой, после чего патчат в памяти пару условных переходов ;) Подобная проверка сможет затруднить такой метод взлома. Зная имена загруженных библиотек, приложение может проверить их на предмет модификации, сравнив образ в памяти с файлом на диске. Это затруднит отлом защиты через сплайсинг вызываемых системных функций. Еще приложение может контролировать, чтобы в его адресное пространство не было загружено никаких посторонних библиотек, например, модулей перехвата различных API Monitor'ов. А поскольку код проверки не использует вызовы API, его легко замаскировать или как-нибудь обфусцировать. Ну а реверсерам, в свою очередь, будет полезно знать о таких методах защиты.

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

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

Get.DLLs.List.Without.API.zip (1,856 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 3677 | Комментариев: 12

Метки: Assembler
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
addhaloka (02.07.2017 в 09:49):
Вот ещё вариант: https://pastebin.com/Snq12jgr
Ничем не отличается от примера, за исключением:
1. win32w.inc вместо win32wx.inc (тут вроде доп. макросы не нужны?)
2. немного по-другому определены данные и секции, в результате размер get_dll_list.exe 1 Кб
pawel97 (27.06.2017 в 18:51):
ManHunter, спасибо! Блин, видел же в какой-то инструкции к лекарству прописывание этой либы в ExcludeFromKnownDlls, но было давно и не думалось, что пригодится в своих целях.
ManHunter (27.06.2017 в 18:13):
Потому что в WinXP эта dll прописана в ветке реестра
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
и имеет приоритет загрузки из системной папки. В Win7 по дефолту не прописана, поэтому грузится первая попавшаяся.
pawel97 (27.06.2017 в 17:56):
"Например, таким способом защитный модуль программы может контролировать, что все используемые системные библиотеки загружены из системной директории."
Можно вопрос на эту тему?
Почему Win7 и выше подгружают лежащую рядом с процессом version.dll, а xp грузит её только из system32? Это как-то изменяется через реестр? Гуглил про всякие Image File Execution Options + procmon'ом смотрел, не нашёл ничего...
ManHunter (22.06.2017 в 15:37):
У меня обычно все ассемблерные исходники в win-1251, а тут что-то прощелкал. Что касается редактора, то я уже давно перешел на Sublime Text 3. На сайте все для этого есть.
DRON (22.06.2017 в 15:22):
Не по теме: это у меня FASMW.EXE неправильный и при редактировании get_dll_list.asm комменты в UTF8 нормально не показывает или вы чем-то другим пользуетесь?
ManHunter (22.06.2017 в 06:52):
Ну теперь точно не отвертеться, даже на лень списать не получится :)))) Заменил исходник в архиве на консольный, от себя добавил ожидание нажатия любой клавиши после вывода списка. Спасибо!
DRON (22.06.2017 в 03:07):
Да дружит консолька с юникодом, чего ей не дружить то:
https://pastebin.com/ykAi6ENF

Редирект в файл (get_dll_list.exe >list) конечно работать не будет, но всяко лучше безумного MessageBox-а: у меня 16 DLL-ок и рука устала давить OK :)
ManHunter (21.06.2017 в 11:08):
Да. Выходит, что проверять лучше BaseAddress. Поправил, спасибо!
Андрей (21.06.2017 в 10:54):
А у меня после вывода библиотек выводится мусорная строка.
Ссылка на картинку https://yadi.sk/i/aPZOR4cE3KKPs3
Значит проверка не корректна?
ManHunter (21.06.2017 в 08:03):
Консолька с юникодом не очень дружит.
user (21.06.2017 в 05:48):
Пожалуй, в примере лучше было бы тупо вываливать листинг в стдоут,
тогда это уже была бы вполне себе утилита.
Понятно, что тогда не получилось бы такого компактного исходника, но.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0083 сек.) / Память: 4.5 Mb
Наверх