• XSS.stack #1 – первый литературный журнал от юзеров форума

Собственный эвристик на коленке [Начало]

TrueMind

RAM
Пользователь
Регистрация
06.10.2013
Сообщения
124
Реакции
1
Собственный эвристик на коленке [Начало]

Каждый раз, проходя по опубликованным (в различных аналитических отчетах антивирусных лабораторий) ссылкам на репорты VirusTotal обнаруживаю для себя, что там прибавляется по нескольку антивирусов с периодичностью в месяц-два.
"Ну и дела" подумал я в очередной раз и решил собрать применяемые мной методы при ресерче малварных и криптованных семплов.
Возможно название для статьи слишком громкое, но потихоньку расширяя её методами автоматической аналитики по различным расширенным признакам PE формата и другим второстепенным признакам мы сможем добиться некого эффекта. Не будем питать иллюзий, что мы делаем очередной (недо)антивирус, а просто определимся целью определять факт того, что перед нами закриптованный семпл.
В этой статье я не буду отсылать вас к википедии при каждом встречающимся здесь определении, таком как: энтропия, частотная характеристика n-gramm, и прочие, т.к. предполагается, что читающий обладает некими знаниями, достаточными для понимая изложенного в статье. В противном случае бегом в википедию и вычитываем значения незнакомых для вас слов и определений.

Для начала давайте окунемся в текущие реалии на этом поприще.
Крипторы скрывают, антивирусы стараются вовремя ( :) ) обнаружить закриптованные семплы, до того, как они будут распространены в миру (англ. in the wild). Борьба меча и щита с вирусов плавно перешла в борьбу крипторов и антивирусов.
Не лишним будет отметить, что сегодняшняя индустрия крипта исполняемых файлов очень широка и разнообразна. До сих пор можно встретить крипторы на VisualBasic 6, но в то же время уже редко встречаются криптованные семплы на ассемблере, скомпилированные на MASM или FASM. И всё таки основная часть проанализированных мной криптованных семплов выпадает на компиляторы C++. Вероятно этот язык и код, генерируемый компилятором наиболее похож на код обычных безвредных программ.
Для антивирусов это означает то, что они с бОльшей вероятностью не смогут установить сигнатуре по маске генерируемого кода, как в случае с крипторами на ассемблере, в которых так или иначе можно выделить некие комбинации (шаблоны кода), по которым рано или поздно начинает определяться всё семейство. Некоторое время назад антивирусы поняли, что на одних сигнатурных (по вайлд-карте, по маске, шаблонные сигнатуры или как бы вы там их не называли) анализаторах им не охватить нужно процента обнаружения и начали вводиться повсеместно всякие интересные технологии, такие как эмуляция кода и среды, эвристика, хипсы (проактивные системы) и т.д.

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


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

Ну думаю достаточно для вступительной речи, пожалуй-с перейдем к действам.
Первый делом будет рассматривать наиважнейший для антивирусов на сегодняшний день метод обнаружения: детект эвристиком.
И так, допустим, что перед нами стоит задача: с максимальной достоверностью определить, что перед нами криптованный файл.
Есть такое понятие как "Zero Value тест", так называемый тест на процентное соотношение кол-ва нулевых байт к остальным 224 байтам.
Перед нами скриншот Exe-Info pe, на котором мы можем видеть диаграмму и процентное соотношение байт (выделено красным)
7201103.png

Собственно теор. часть я определил выше, настало время собственной реализации данной тенхик. (В принципе мне одинаково близки как паскаль, так и си, но конкретно в данном случае решил писать на паскале.) Приступим-с.

На вход в функцию подаем считанный буфер файлы и его размер.
На выходе значение в double.
Код:
function CalcZeroFreq(pBuffer:string; buf_size:integer):double;
var
  zero_count:integer;
  i:integer;
begin
  zero_count:=0;
  for i:=0 to buf_size do begin
    if pBuffer[i]=#0 then inc(zero_count);
  end;
  result := zero_count/buf_size;
end;
Тут всё очень просто и думаю даже нет смысла комментить строки, т.к. выше суть уже описал.

И пример использования:
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  FileHandle:Thandle;
  Size:cardinal;
  Buffer:string;
  BytesRead:dword;
  iNullPercentage:double;
  iEntroTest:double;
begin
label1.caption := '';
label2.caption := '';
if ((edit1.Text <> '....') and (edit1.Text <> '')) then begin
  FileHandle := CreateFile(PChar(od.filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  Size := GetFileSize(FileHandle, nil);
  SetLength(Buffer, Size);
  ReadFile(FileHandle, Buffer[1], Size, BytesRead,nil);
  CloseHandle(FileHandle);

  //////////////////////////
  //    zero value test   //
  iNullPercentage := CalcZeroFreq(Buffer, size);

  if iNullPercentage <= 0.0250 then
	label1.caption := '[*] Zero Value Test: Strong packed';

	if ((iNullPercentage > 0.0250) and (iNullPercentage <= 0.055)) then
  label1.caption := '[*] Zero Value Test: Packed';


	if ((iNullPercentage > 0.055) and (iNullPercentage <= 0.150)) then
	label1.caption := '[*] Zero Value Test: Crypted';

	if ((iNullPercentage > 0.150) and (iNullPercentage <= 0.255)) then
	label1.caption := '[*] Zero Value Test: Not Packed';

	if (iNullPercentage > 0.255) then
  label1.caption := '[*] Zero Value Test: Very Not Packed';
Всё достаточно прозрачно: читаем файл, узнаем его размер, высчитываем с помощью нашей функции соотношение нулевых байт к остальным байтам и сравниваем с эталонной таблицей, как результат выводим диагноз: Strong packed, Packed, Crypted, Not Packed. Антивирусы, при положительном результате поставили бы на такой файл +1 балл подозрительности.
Пульнем тот же файлик, который кормили Exe-Info pe, в нашу утилитку, видим:
7148878.png


Еще один очень важный момент при сканировании файла эвристиком, это проверка энтропии (степени эффективности хранения информации). Проверяется значение энтропии как всего файла, так и каждой секции отдельно.
Код:
function CalcEntropyForBuffer(Buffer:Pointer; BufferSize:DWORD):Double;
const
  DbLog: Double = 1.4426950408889634073599246810023;
var
  Entropy: Double;
  Entries: array[0..255] of DWORD;
  i: DWORD;
  Temp: Double;
begin
  Entropy := 0.00;
  ZeroMemory(@Entries, SizeOf(Entries));
  for i := 0 to (BufferSize - 1) do
    Inc(Entries[PByte(DWORD(Buffer) + i)^]);
      for i := 0 to 255 do
        begin
            Temp := Entries[i] / BufferSize;
            if (Temp > 0) then
              Entropy := Entropy + Temp * (Ln(Temp) * DbLog);
        end;
  Result := Entropy;
end;
Данная функция рассчитывает значение энтропии для нашего буфера. На скриншоте выше показан результат работы данной функции.

Пример использования:
Код:
//////////////////////////
  //   entropy test   //
  iEntroTest := CalcEntropyForBuffer(@Buffer[1], size);

  if ((iEntroTest > 6)) then
  label2.caption := '[*] Entropy Test: Packed : '+FloatToStr(iEntroTest)
  else
  label2.caption := '[*] Entropy Test: Not Packed : '+FloatToStr(iEntroTest);
  end;

Ну что-ж, для начала пищи для размышления думаю вам хватит. В сл. частях разберем обнаружение по частотным характеристикам n-gramms, а также обнаружение по энтропии каждой отдельной секции.

Если кому интересно потыкать, могу пульнуть готовый сурс и скомпилированный бинарь.

Заметки на полях от TrueMind, специально для xss.pro/ и panteon.nl ©.
Спасибо за внимание!
 
Интересная статья, но есть один вопрос: Откуда взялись следующие константы?
 if iNullPercentage <= 0.0250 then
label1.caption := '[*] Zero Value Test: Strong packed';

if ((iNullPercentage > 0.0250) and (iNullPercentage <= 0.055)) then
 label1.caption := '[*] Zero Value Test: Packed';


if ((iNullPercentage > 0.055) and (iNullPercentage <= 0.150)) then
label1.caption := '[*] Zero Value Test: Crypted';

if ((iNullPercentage > 0.150) and (iNullPercentage <= 0.255)) then
label1.caption := '[*] Zero Value Test: Not Packed';

if (iNullPercentage > 0.255) then
 label1.caption := '[*] Zero Value Test: Very Not Packed';
 
Хогвардс, школа магии )
а в качестве палочки оллидбг)
 
К примеру, при поиске TrueCrypt контейнеров смотрят на распределение хи-квадрат ( http://alglib.sources.ru/specialfunctions/...s/chisquare.php ),
также есть условие по размеру файла, а именно: file_size % 512 == 0

к остальным 224 байтам.
254 байтам*

Почему подсчитываешь нули, какова эффективность данного теста перед расчётом энтропии по Шеннону ?
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх