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

warning

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

 

Как устроены WinAPI Indirect Calls

За­чем изоб­ретать что‑то новое, если есть такая тех­ника, как Indirect Calls для вызовов WinAPI? У нее есть свои силь­ные сто­роны, она прос­та в реали­зации и хорошо докумен­тирова­на. В двух сло­вах напом­ню, в чем ее суть.

Indirect Syscalls вызыва­ет NTAPI не нап­рямую через ntdll.dll, а через собс­твен­ный код, который эму­лиру­ет вызов фун­кции NTAPI: фор­миру­ется стек аргу­мен­тов, в eax заносит­ся номер сис­темно­го сер­виса для его вызова, далее выпол­няет­ся переход по адре­су инс­трук­ции syscall в самой ntdll.dll. Вот условный код:

// Ранее сформирован стек аргументов, далее сам вызов WinAPI из ntdll.dll
mov r10, rcx
mov eax, <syscall_number>
jmp <address_of_syscall_instruction_in_ntdll>

Со сто­роны выг­лядит хорошо, и это работа­ло, пока EDR не научи­лись исполь­зовать трас­сиров­ку событий ETW для рас­крут­ки сис­темно­го сте­ка вызовов. Все дело в том, что тех­ника Indirect Syscalls поз­воля­ет избе­жать детек­тирова­ния на уров­не поль­зователь­ско­го режима, но оставля­ет сле­ды в сте­ке вызовов. EDR может ана­лизи­ровать стек, рас­кру­чивая его, и обна­ружи­вать эти ано­малии.

 

Стек, фреймы и EDR

Для даль­нейше­го понима­ния при­дет­ся нем­ного пог­рузить­ся в мат­часть и понять, как фор­миру­ются и работа­ют сте­ковые фрей­мы. Сте­ковый фрейм (или кадр сте­ка) — это область памяти в сте­ке, выделя­емая для выпол­нения каж­дой фун­кции в прог­рамме. Он содер­жит такие дан­ные, как парамет­ры фун­кции, локаль­ные перемен­ные, зна­чения регис­тров, адрес воз­вра­та. Прос­той при­мер кода:

void func1() {
int x = 10;
func2(x);
...
...
}
void func2(int y) {
int z = y + 5;
}

При вызове func2(x) будет сфор­мирован фрейм:

  • RSP — перемен­ная z;
  • RBP — базовый ука­затель пре­дыду­щего фрей­ма;
  • RBP + 8 — ука­затель на сле­дующую инс­трук­цию в func1() (адрес воз­вра­та);
  • RBP + 16 — параметр фун­кции func2.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии