Снимаем крючки. Познаем анхукинг ntdll.dll

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

  • Снятие хука через чтение библиотеки с диска
  • Снятие хука через KnownDlls
  • Снятие хука через приостановленный процесс
  • Снятие хука через подгрузку ntdll.dll с удаленного веб-сервера
  • Выводы

Средс­тва защиты, в час­тнос­ти EDR, любят ста­вить хуки. Хук — это спе­циаль­ная инс­трук­ция, которая поз­воля­ет перех­ватить поток управле­ния прог­раммы при вызове опре­делен­ной фун­кции и в резуль­тате кон­тро­лиро­вать, отсле­живать и изме­нять дан­ные, передан­ные этой фун­кции. В этой статье я покажу, как про­водить обратный про­цесс — анху­кинг.

info

Под­робнее про хуки ты можешь узнать из ста­тей «Вол­шебные хуки. Как перех­ватывать управле­ние любой прог­раммой через WinAPI» и «Мел­комяг­кие хуки: Microsoft Detours — чес­тное средс­тво для нас­тояще­го хакера».

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

При­мер хука

Здесь EDR пос­тавил хук на NtAllocateVirtualMemory(). Эта фун­кция будет пос­ледней в User Mode, она вызыва­ется лишь для ини­циали­зации сис­темно­го вызова и выделе­ния памяти путем обра­щения к ядру. В сто­ковой кон­фигура­ции, ког­да хука нет, никаких безус­ловных jmp-перехо­дов быть не дол­жно. Тут мы видим иную ситу­ацию: переход как раз таки есть, поток управле­ния отда­ется непонят­но кому и непонят­но куда. Поэто­му нам как ата­кующим, да и прос­то что­бы укло­нить­ся от обна­руже­ния, нуж­на опе­рация анху­кин­га, которая сни­мет этот хук, и, как следс­твие, средс­тво защиты потеря­ет кон­троль над потоком выпол­нения прог­раммы.

От­мечу лишь, что подоб­ный спо­соб обхо­да хуков — один из мно­жес­тва. Мож­но, нап­ример, совер­шать Direct- и Indirect-сис­колы, но сто­ит пом­нить, что получит­ся обой­ти толь­ко хуки, которые сто­ят в User Mode. Если средс­тво защиты при­меня­ет хуки Kernel Mode (нап­ример, SSDT Hooking), то подоб­ные методы ока­жут­ся бес­полез­ны. На будущее: SSDT — это спе­циаль­ная таб­лица, бла­года­ря которой сопос­тавля­ются сис­кол и дей­ствие ядра Windows. Есть, конеч­но, Kernel Patch Protection, который меша­ет уста­нав­ливать подоб­ные хуки, но это уже сов­сем дру­гая исто­рия.

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

warning

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

 

Снятие хука через чтение библиотеки с диска

Этот метод мож­но счи­тать одним из самых прос­тых. Он осно­ван на том, что биб­лиоте­ка ntdll.dll под­гру­жает­ся в память так же, как находит­ся на дис­ке. При­чем хуки уста­нов­лены непос­редс­твен­но в памяти, на дис­ке образ девс­твен­но чист. Поэто­му мы дол­жны будем лишь счи­тать биб­лиоте­ку с дис­ка, дос­тать из нее PE-сек­цию .text (в ней находит­ся код), а пос­ле переза­писать сек­цию .text хук­нутой биб­лиоте­ки сек­цией, счи­тан­ной с дис­ка.

Ал­горитм сня­тия хука

Мы будем исполь­зовать фун­кции ReadFile() и MapViewOfFile(), и EDR может отсле­живать их, поэто­му есть риск, что наша ntdll.dll, заг­ружен­ная с дис­ка, будет изме­нена при попыт­ке под­гру­зить ее содер­жимое в прог­рамму. Поэто­му при­дет­ся исполь­зовать иной спо­соб сня­тия хука, нап­ример тащить ntdll.dll с неко­его уда­лен­ного сер­вера. Этот алго­ритм реали­зуем поз­же. За идею боль­шое спа­сибо Раль­фу.

Итак, сна­чала нуж­но счи­тать содер­жимое биб­лиоте­ки ntdll.dll. Нач­нем со стан­дар­тной фун­кции ReadFile(). По умол­чанию ntdll.dll лежит в сис­темной пап­ке WindowsSystem32. Пред­лагаю соз­дать фун­кцию, которая будет воз­вра­щать буфер с содер­жимым ntdll.dll.

#define NTDLL "NTDLL.DLL"BOOL ReadNtdllFromDisk(OUT PVOID* ppNtdllBuf) { CHAR cWinPath[MAX_PATH / 2] = { 0 }; CHAR cNtdllPath[MAX_PATH] = { 0 }; HANDLE hFile = NULL; DWORD dwNumberOfBytesRead = NULL, dwFileLen = NULL; PVOID pNtdllBuffer = NULL; if (GetWindowsDirectoryA(cWinPath, sizeof(cWinPath)) == 0) { printf("[!] GetWindowsDirectoryA Failed With Error : %d n", GetLastError()); goto EndOfFunc; } sprintf_s(cNtdllPath, sizeof(cNtdllPath), "%s\System32\%s", cWinPath, NTDLL); hFile = CreateFileA(cNtdllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("[!] CreateFileA Failed With Error : %d n", GetLastError()); goto EndOfFunc; } dwFileLen = GetFileSize(hFile, NULL); pNtdllBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwFileLen); if (!ReadFile(hFile, pNtdllBuffer, dwFileLen, &dwNumberOfBytesRead, NULL) || dwFileLen != dwNumberOfBytesRead) { printf("[!] ReadFile Failed With Error : %d n", GetLastError()); printf("[i] Read %d of %d Bytes n", dwNumberOfBytesRead, dwFileLen); goto EndOfFunc; } *ppNtdllBuf = pNtdllBuffer;EndOfFunc: if (hFile) CloseHandle(hFile); if (*ppNtdllBuf == NULL) return FALSE; else return TRUE;}

Ос­тает­ся про­верить, что наш код вер­но работа­ет. Если ты пишешь в Visual Studio, то откры­вай пункт «Отладка → Парамет­ры» и ставь две галоч­ки, что­бы мож­но было видеть содер­жимое памяти.

Вклю­чение показа содер­жимого памяти

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *