Функция ntdll dll перехвачена

Функция ntdll dll перехвачена

Как известно, OC Windows NT целиком построена на системе DLL (динамически загружаемых библиотек). Система предоставляет приложениям сервисные API функции, с помощью которых оно может взаимодействовать с системой. Перехват API функций позволяет обойти многие ограничения системы и делать с ней практически что угодно.

API функции представляют и себя ничто иное, как функции в системных DLL. Любой процесс в системе обязательно имеет в своем адресном пространстве Ntdll.dll, где располагаются функции Native API — базовые функции низкоуровневой работы с системой, функции Kernel32.dll являются переходниками к более мощным функциям Ntdll, следовательно, целесообразно будет перехватывать именно функции Native API. Проблема в том, что Native API функции не документированы в SDK, но узнать модель их вызова можно дизассемблируя Kernel32.dll. Нельзя утверждать, что адреса функций в системных библиотеках не изменяются в зависимости от версии ОС, ее сборки либо даже конкретной ситуации. Это происходит из-за того, что предпочитаемая база образа библиотеки (dll preferred imagebase) является константой, которую можно изменять при компиляции. Более того, совсем не обязательно, что dll будет загружена именно по предпочитаемому адресу, — этого может не произойти в результате коллизии с другими модулями, динамически выделенной памятью и т.п. Поэтому статический импорт функций происходит по имени модуля и имени функции (либо ее номера — ординала), предоставляемой этим модулем. Загрузчик PE файла анализирует его таблицу импорта и определяет адреса функций, им импортируемых. В случае, если в таблице импорта указана библиотека, не присутствующая в контексте загружаемой программы, происходит ее отображение в требуемый контекст, настройка ее образа и ситуация рекурсивно повторяется. В результате в требуемом месте определенной секции PE файла (имеющей, как минимум, атрибуты "readable" и "initialized data") заполняется массив адресов импортируемых функций. В процессе работы каждый модуль обращается к своему массиву для определения точки входа в какую-либо функцию. Следовательно существуют два основных метода перехвата API вызовов: изменение точки входа в таблице импорта и изменение начальных байт самой функции (сплайсинг функции).

Изменение таблиц импорта:

Этот метод выглядит так. Определяется точка входа перехватываемой функции. Составляется список модулей, в настоящий момент загруженных в контекст требуемого процесса. Затем перебираются дескрипторы импорта этих модулей в поиске адресов перехватываемой функции. В случае совпадения этот адрес изменяется на адрес нашего обработчика.

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

Недостаток этого метода в том, что приложения могут сохранить адрес функции до перехвата, и затем вызывать её минуя обработчик. Также можно получить адрес функции используя GetProcAddress из Kernel32.dll. Из-за этого недостатка я считаю этот метод бесперспективным в применении и подробно рассматривать его не буду.

Сплайсинг функции:

Этот метод состоит в следующем: определяется адрес перехватываемой функции, и первые 5 байт её начала заменяются на длинный jmp переход по адресу обработчика перехвата.

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

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

Дата добавления: 2015-01-29 ; просмотров: 20 ; Нарушение авторских прав

Содержание статьи

У Стивена Кинга есть произведение «Потаенное окно, потаенный сад». Не могу сказать, что я люблю творчество этого писателя, но если ты не читал эту книгу, настоятельно советую найти и прочесть.

Очень занимательная и одновременно пугающая книга. В ней со всей присущей Стивену Кингу ужасающей красотой изложения рассказывается о том, какие тайны может хранить в себе сознание любого человека.

Вот и сегодня мы, наверное, не будем разговаривать на какую-то конкретную тему. Мы просто немного полазаем в потаенном саду Windows, забравшись туда через потаенное окно дебаггера :). Я попробую рассказать о скрытых местах, странностях и неизвестностях операционной системы Windows. Эти знания помогут тебе, как программисту, лучше знать, понимать и использовать эти самые потаенные места в своих грязных целях.

Введение

Даже по прошествии многих лет, потраченных на изучение внутренностей операционной системы и системного кодинга, понимаешь, что постичь все тонкости ОС вряд ли удастся. Я не имею в виду именно себя — такого мнения придерживаются многие программисты, с которыми я знаком.

При этом зачастую единственным инструментом, позволяющим выпытать те или иные секреты операционной системы, становится отладчик или дебаггер. Хотя не все любят возиться с отладчиком, положения дел это не меняет — если хочешь находить, простите за каламбур, потаенные окна в Windows — без него не обойтись. Итак, начнем.

Загадочный параметр LPRESERVED в DLLMAIN

Всем нам известна точка входа при старте библиотек — DllMain:

BOOL WINAPI DllMain(
__in HINSTANCE hinstDLL,
__in DWORD dwReason,
__in LPVOID lpReserved
);

Принимает она (точка входа) три параметра. С первыми двумя все понятно, но как быть с третьим? И действительно, зачем нужен этот параметр lpReserved, если нигде в коде при инициализации библиотеки он больше не используется? Оказывается не все так просто, как пытается это показать Microsoft.

MSDN утверждает, что этот параметр используется при загрузке/выгрузке библиотеки; в частности, при статических операциях библиотеки этот параметр содержит отличное от нуля значение. И, наоборот, при динамических операциях lpReserved будет равным нулю. Открою страшную тайну: lpReserved есть ничто иное, как указатель на контекст стартующего процесса, который грузит библиотеку! Подробности таковы: при старте нового потока ядро ставит его в очередь для исполнения в виде APC — AsyncProcedureCall, который передается в функцию LdrInitializeThunk, который вызывается Ntdll.dll.

Одним из параметров, который передается LdrInitializeThunk, является указатель на структуру CONTEXT, которая описывает начальное состояние потока — регистры, данные и т.п. После выполнения APC, контроль передается LdrInitializeThunk. Раз уж исполнение нового потока начинается с вызова ntdll!LdrInitializeThunk, то этой функции передается стартовый адрес, определенный функцией CreateThread. Таким образом становится понятно (а уж под отладчиком — тем более!), что CreateThread должен передать через APC в вызов LdrInitializeThunk параметры старта процесса.

Подведем итоги: в случае, если dwReason равен DLL_PROCESS_ATTACH (при загрузке библиотеки), lpReserved равен NULL для динамической загрузки и non-NULL для статической загрузки.

В случае, если fdwReason равен DLL_PROCESS_DETACH (при выгрузке библиотеки), lpReserved равен NUL при вызове FreeLibrary и при ошибке загрузки DLL, и nonNULL — при окончании процесса. Зачем Microsoft скрывать этот факт? На самом деле, я бы тоже его скрыл :). Подумай сам, сколько возможностей подмены контекста открывается при этом! Что? Ты никогда не слышал о контексте процесса? И системный вызов SetThreadContext тебе тоже ни о чем не говорит? Окей, рассмотрим. Во-первых, контроль над структурой CONTEXT даст нам контроль над регистрами процессора. Все регистры процессора при старте указываются в структуре CONTEXT (смотри описание этой структуры). Это могут быть DEBUG-регистры для контроля над определенным приложением или же установки перехватов вызовов функций.

Или, кстати, установки флага TF в регистре EFLAGS Во-вторых, путем изменения lpReserved->Eip можно изменить точку старта библиотеки. Эта особенность также может быть использована в определении версии ОС, которая используется на целевой машине путем выбора точки входа в зависимости от версии ОС. Незаменимое свойство для обеспечения переносимости кода, кстати.

Аналогичные адреса загрузки dll

И действительно, если ты обращал внимание, такие библио теки как ntdll.dll, kernel32.dll и user32.dll для всех процессов всегда загружаются по одному и тому системному адресу, хотя Microsoft это никак не объясняет. Почему?

Как ты знаешь, указанные библиотеки представляют программисту набор системных функций для работы с системой. К примеру, ntdll.dll является самой важной из юзермодных библиотек. Она представляет собой своеобразную заглушку для вызова системных сервисов. И она должна быть загружена по одному и тому же адресу именно по этой причине. Например, создание любого юзермодного потока всегда происходит через вызов функции ntdll!LdrInitializeThunk. Функция ntdll!KiUserApcDispatcher нужна системе для того, чтобы поставить в очередь исполнение юзермодных асинхронных вызовов. Ядро операционной системы определяет адреса этих функций еще на стадии инициализации системы. И, так как ядро использует скэшированные указатели на эти функции (для быстродействия), ntdll.dll уже не может быть загружена по другим адресам. Kernel32.dll не может быть загружен по различным адресам, потому что большое количество предоставляемых этой библиотекой сервисов используются системой для кросспроцессовых инъекций кода. Например, kernel32.dll ответственна за обработчик событий консоли (что делает команда Ctrl+C в консоли, помнишь?). Так как консоль могут запустить многие программы, адрес обработчика Ctrl+C должен быть одним и тем же. Ну а user32.dll постоянно загружается по одному и тому же адресу по той простой причине, что она предоставляет кучу сервисов, используемых win32k.sys — драйвера, реализующего оконную подсистему Windows.

Указатели на эти функции win32k.sys получает через вызов NtUserInitializeClientPfnArrays во время загрузки.

Однопоточность? Не тут-то было!

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

Потому что многие (если не все) Win32-приложения на самом деле являются многопотоковыми программами, даже если их разработчик утверждает обратное. К примеру, при старте программы сервисом подсистемы CSRSS в программе по умолчанию создается отдельный поток для обработки консольных событий типа Ctrl+C/Ctrl+Break. Во-вторых, большинство Win32-API-функций для выполнения своего кода используют отдельные потоки. Например, вызов WSAAsyncGetHostByName исполь зует синхронный вызов gethostbyname в отдельном потоке, после чего возвращает результаты запрашивающему через оконные сообщения.

Разница между нативными x86версиями библиотек и их WOW64-аналогами

Механизм Wow64 включает в себя полный набор 32-битных системных dll, реализующий Win32 API-функции (для их использования Wow64-программами). Так какова же разница между «нормальными» 32-битными dll и их Wow64-версиями?

На 64-битных версиях Windows разницы между такими библиотеками нет — большинство dll являют собой 32-битные копии с 32-битной версии операционной системы. К примеру, Wow64-библиотека ws2_32.dll на Vista x64 — тот же самый файл, что и 32-битная ws2_32.

dll на Vista x86. Вместе с тем, некоторые dll отличаются очень значительно, к примеру, ntdll.dll. Если мы глянем сквозь призму отладчика на x86 версию ntdll.dll, то легко сможем увидеть, что системный вызов уходит в ядро системы через так называемый SystemCallStub в структуре SharedUserData:

lkd> u ntdll!NtClose
ntdll!ZwClose:
mov eax,30h
mov edx,offset SharedUserData!SystemCallStub
call dword ptr [edx]
ret 4

В Wow64-версии ntdll картина разительно отличается. Вызов системного сервиса происходит через поле по смещению 0xc0 в 32-битной структуре TEB (Thread Environment Block):

lkd> u ntdll!NtClose
ntdll!ZwClose:
mov eax,0Ch
xor ecx,ecx
lea edx,[esp+4]
call dword ptr fs:[0C0h]
ret 4

В свою очередь, раскрываем структуру TEB и там по смещению 0xc0 видим поле, помеченное как “WOW32Reserved”:

lkd> dt ntdll!_TEB
+0x000 NtTib : _NT_TIB
[skip. ]
+0x0c0 WOW32Reserved : Ptr32 Void

Кстати, в качестве лирического отступления от темы хочу заметить, что если ты планируешь использовать 32-битные программы под Wow64, будь очень внимателен при использовании таких функций как GetThreadContext/SetThreadContext, и вот почему. Данные функции требуют дополнительных привилегий при исполнении в контексте Wow64. В частности, им нужен доступ к данным THREAD_QUERY_INFORMATION.

12 способов завершить процесс

Чтобы ты всегда мог выйти победителем из социалистического соревнования на тему «Кто знает больше способов грохнуть процесс», проведенного в кругу друзей, любимый журнал заботливо подгоняет тебе целых 12 методов:

  1. Использовать функции TerminateProcess или NtTerminateProcess — понятно без лишних слов, правда, они всегда перехватываются аверами для своей защиты;
  2. Использовать CreateRemoteThread с вызовом ExitProcess. Для этого тебе нужно будет найти адрес ExitProcess внутри того процесса, который ты хочешь завершить;
  3. Использовать комбинацию NtQuerySystemInformation или toolhelp32 с вызовом TerminateThread or NtTerminateThread. Все предельно просто — находишь все потоки искомого процесса и завершаешь их вызовом TerminateThread (NtTerminateThread);
  4. Вызвать NtQuerySystemInformation или toolhelp32, после чего вызовом SetThreadContext установить регистр EIP так, чтобы он указывал на ExitProcess;
  5. В цикле от 0 до 4096 вызвать функцию DuplicateHandle с параметрами TargetProcess и TargetProcessHandle равными NULL, а Options равным 0x1. Это закроет если не все, то почти все хендлы открытого процесса. Что интересно — этот метод прекрасно действует против сложных программ и систем, типа антивирусов, однако не сможет грохнуть notepad.exe;
  6. Довольно громоздкий способ — можно вызвать последовательно CreateJobObject, AssignProcessToJobObject и TerminateJobObject;
  7. Сложный способ, больше известный в среде дебаггеров — вызываем последовательно NtCreateDebugObject для процесса, затем NtDebugActiveProcess, после чего закрываем хендл дебаг-объекта (читай — процесса) вызовом CloseHandle;
  8. Оригинальный способ — последовательно для всего региона памяти процесса вызываем VirtualQueryEx с параметром PAGE_NOACCESS и VirtualProtectEx. Процесс тихо умрет, когда все страницы памяти станут недоступными;
  9. Топорный способ — открываем память процесса VirtualQueryEx, после чего вызовом WriteProcessMemory начинаем писать в память процесса всякую нечитаемую фигню;
  10. Еще один оригинальный способ — до посинения вызывать VirtualQueryEx. Когда кончится память под выделение, процесс умрет сам;
  11. Ядерная функция — PsTerminateProcess (PspTerminateProcess). Так как ядром она не экспортируется, вызвать ее можно только путем сканирования ядра на предмет определенной сигнатуры;
  12. Еще одна неэкспортируемая фукнция — PspTerminateThreadByPointer. Ищется в ядре аналогичным образом, путем сканирования памяти.

Кстати, код, реализующий поиск и перехват PspTerminateThreadByPointer для защиты твоего процесса от убийства таким способом, ты сможешь найти на диске.

Заключение

Читать доки, бесспорно, очень полезно. Поскольку они — рулез. Однако практика показывает, что самые вкусности и сочные куски ОС часто бывают недокументированными, и разработчики Windows очень неохотно раскрывают нам эти секреты. Но все тайное всегда становится явным. Так что дерзай! Удачного компилирования, и да пребудет с тобой Сила!

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

  • функции-заглушки, обеспечивающие переходы от диспетчера системных служб к системным службам исполняющей системы Windows;
  • вспомогательные внутренние функции, используемые подсистемами, DLL-библиотеками подсистем и другими исходными образами.

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

К этой группе относятся более чем 400 функций, среди которых NtCreateFile, NtSetEvent и т. д. Как уже отмечалось, основная часть возможностей, присущих данным функциям, доступна через Windows API. Но некоторые возможности недоступны и предназначены для использования только внутри операционной системы.

Для каждой из этих функций в Ntdll содержится точка входа с именем, совпадающим с именем функции. Код внутри функции содержит зависящую от конкретной архитектуры инструкцию, осуществляющую переход в режим ядра для вызова диспетчера системных служб, который после проверки ряда параметров вызывает настоящую системную службу режима ядра, реальный код которой содержится в файле Ntoskrnl.exe.

Ntdll также содержит множество вспомогательных функций, таких как загрузчики образов (префикс Ldr), диспетчер динамической области памяти и функции обмена данными между процессами подсистемы Windows (префикс Csr). Ntdll включает также общие подпрограммы библиотеки времени выполнения (префикс Rtl), поддержку отладки в пользовательском режиме (префикс DbgUi) и отслеживания событий для Windows (EventTracingforWindows) (префикс Etw), а также диспетчер асинхронных вызовов процедур и исключений пользовательского режима (APC).

И наконец, в Ntdll находится небольшой поднабор подпрограмм времени выполнения языка C (CRT), ограниченный подпрограммами, являющимися частью строковых и стандартных библиотек (в качестве примера можно привести подпрограммы memcpy, strcpy, itoaи т. д.).

Читайте также:

  1. I. средняя скорость; II. мгновенная скорость; III. вектор скорости, выраженный через проекции на оси; IV. величина (модуль) скорости.
  2. L – класс линейных функций.
  3. Microsoft Windows Embedded
  4. MS-DOS, Unix, Windows.
  5. RTX для Windows NT
  6. Алгоритм минимизации функций в классе нормальных форм
  7. Анатомия модульі бойынша тест сұрақтары
  8. Анатомо-морфологическая база высших психических функций
  9. Арифметические операции над непрерывными функциями. Композиция непрерывных функций
  10. Базовым принципом концепции МСС является отделение друг от друга функций переноса и коммутации, функций управления вызовом и функций управления услугами.
Ссылка на основную публикацию
Фиксированная шапка сайта при прокрутке
Допустим у вас важная информация например контакты находятся в шапке и вы хотите что бы они всегда были на веду...
Удаление последнего элемента списка
Введение. Основные операции О дносвязный список – структура данных, в которой каждый элемент (узел) хранит информацию, а также ссылку на...
Удаление дубликатов фотографий на русском бесплатно
Здравствуйте Уважаемый Друг. У каждого из нас на компьютере хранится большое количество различных фотографий изображений и тому подобных картинок. Парой...
Фиксированное меню при скролле
Создаём эффект залипания при прокручивании страницы на блоках меню навигации, бокового виджета и меню с помощью jQuery и без него....
Adblock detector