Подтвердить что ты не робот

Примеры метаморфических кодов

Я понимаю концепцию Полиморфный и Метаморфический код, но я недавно прочитал страницу Википедии на обоих ( по какой причине я раньше этого не делал!). Теперь я действительно хочу поработать над написанием метаморфического кода для себя.

Я не владею языком, многолюбием. Я знаю некоторые скрипты PHP, MySQL, c/С++, Java, Bash, Visual Basic 6, VBScripting, Perl, JavaScript.

Может ли кто-нибудь предоставить пример метаморфического кода на любом из этих языков. Я бы хотел увидеть рабочий пример, даже если выход программы - это просто "Hello World", чтобы понять на примере, как это происходит (я изо всех сил пытаюсь теоретировать, как эти методы могут быть достигнуты только с помощью умственной мысли). Любой язык будет делать действительно, это только предпочтительные.

Кроме того, поиск в Интернете только вернул ограниченное число примеров в c/С++ (даже не полные рабочие примеры, более фрагментарные фрагменты кода), так как другие языки, которые я предложил, не являются достаточно низкими, чтобы имеют силу/гибкость, необходимые для создания метаморфического кода?

4b9b3361

Ответ 1

Ниже приведен пример того, что, по моему мнению, классифицируется как метаморфический код, написанный на C. Я боюсь, что у меня нет большого опыта написания переносимого кода C, поэтому для компиляции на других платформах может потребоваться некоторая модификация (Я использую старую версию Borland в Windows). Кроме того, он полагается на целевую платформу, являющуюся x86, поскольку она связана с генерированием машинного кода. Теоретически он должен компилироваться на любой ОС x86.

Как это работает

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

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

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

Каждая последовательность начинается с операции push в определенном регистре, набора инструкций, которые изменяют этот регистр, а затем поп-операции для восстановления регистра до его начального значения. Чтобы все было просто, в исходном источнике все последовательности - это просто PUSH EAX, восемь NOP и POP EAX. Тем не менее, во всех последующих поколениях приложения последовательности будут полностью случайными.

Объяснение кода

Я разделил код на несколько частей, чтобы попытаться объяснить его шаг за шагом. Если вы хотите скомпилировать его самостоятельно, вам просто нужно объединить все части.

В первый довольно стандартный стандарт входит:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

Далее мы определяем для различных кодов операций x86. Обычно они объединяются с другими значениями для генерации полной инструкции. Например, PUSH определяет (0x50) сам по себе PUSH EAX, но вы можете получить значения для других регистров, добавив смещение в диапазоне от 0 до 7. То же самое для POP и MOV.

#define PUSH 0x50
#define POP  0x58
#define MOV  0xB8
#define NOP  0x90

Последние шесть представляют собой префиксные байты нескольких двухбайтовых кодов операций. Второй байт кодирует операнды и будет объяснен более подробно позже.

#define ADD  0x01
#define AND  0x21
#define XOR  0x31
#define OR   0x09
#define SBB  0x19
#define SUB  0x29

const unsigned char prefixes[] = { ADD,AND,XOR,OR,SBB,SUB,0 };

JUNK - это макрос, который вставляет нашу последовательность мусорных операций в любом месте в коде. Как я уже объяснял ранее, изначально он просто писал PUSH EAX, NOP и POP EAX. JUNKLEN - количество NOP в этой последовательности, а не полная длина последовательности.

И если вы не знаете, __emit__ - это псевдофункция, которая вводит литеральные значения непосредственно в код объекта. Я подозреваю, что вам может понадобиться порт, если вы используете другой компилятор.

#define JUNK __emit__(PUSH,NOP,NOP,NOP,NOP,NOP,NOP,NOP,NOP,POP)
#define JUNKLEN 8

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

unsigned char *code;
int codelen;

Далее у нас есть простая функция, которая будет читать наш объектный код в памяти. Я никогда не освобождаю эту память, потому что мне просто все равно.

Обратите внимание на вызовы макроса JUNK, вставленные в случайные точки. Вы увидите намного больше этого кода. Вы можете вставлять их почти в любом месте, но если вы используете настоящий компилятор C (в отличие от С++), он будет жаловаться, если вы попытаетесь поместить их перед объявлениями переменных или в промежутках.

void readcode(const char *filename) {
  FILE *fp = fopen(filename, "rb");    JUNK;
  fseek(fp, 0L, SEEK_END);             JUNK;
  codelen = ftell(fp);
  code = malloc(codelen);              JUNK;
  fseek(fp, 0L, SEEK_SET);
  fread(code, codelen, 1, fp);         JUNK;
}

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

void writecode(const char *filename) {
  FILE *fp;
  int lastoffset = strlen(filename)-1;
  char lastchar = filename[lastoffset];
  char *newfilename = strdup(filename);  JUNK;
  lastchar = '0'+(isdigit(lastchar)?(lastchar-'0'+1)%10:0);
  newfilename[lastoffset] = lastchar;
  fp = fopen(newfilename, "wb");         JUNK;
  fwrite(code, codelen, 1, fp);          JUNK;
  fclose(fp);
  free(newfilename);
}

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

В зависимости от того, сколько места у нас есть, мы можем быть ограничены инструкциями, которые мы можем выписать, иначе мы произвольно выбираем, является ли это NOP, MOV или одним из других. NOP - это всего лишь один байт. MOV - пять байтов: наш код MOV (с добавленным параметром reg) и 4 случайных байта, представляющих число, перемещенное в регистр.

Для двух байтовых последовательностей первое является одним из наших префиксов, выбранных случайным образом. Второй - это байт в диапазоне от 0xC0 до 0xFF, где наименее значащие 3 бита представляют собой первичный регистр, т.е. Который должен быть установлен на значение нашего параметра reg.

int writeinstruction(unsigned reg, int offset, int space) {
  if (space < 2) {
    code[offset] = NOP;                         JUNK;
    return 1;
  }
  else if (space < 5 || rand()%2 == 0) {
    code[offset] = prefixes[rand()%6];          JUNK;
    code[offset+1] = 0xC0 + rand()%8*8 + reg;   JUNK;
    return 2;
  }
  else {
    code[offset] = MOV+reg;                     JUNK;
    *(short*)(code+offset+1) = rand();
    *(short*)(code+offset+3) = rand();          JUNK;
    return 5;
  }
}

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

Если он находит правильное совпадение, он возвращает длину инструкции, в противном случае он возвращает ноль.

int readinstruction(unsigned reg, int offset) {
  unsigned c1 = code[offset];
  if (c1 == NOP)
    return 1;                     JUNK;
  if (c1 == MOV+reg)
    return 5;                     JUNK;
  if (strchr(prefixes,c1)) {
    unsigned c2 = code[offset+1]; JUNK;
    if (c2 >= 0xC0 && c2 <= 0xFF && (c2&7) == reg)
      return 2;                   JUNK;
  }                               JUNK;
  return 0;
}

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

void replacejunk(void) {
  int i, j, inc, space;
  srand(time(NULL));                                 JUNK;

  for (i = 0; i < codelen-JUNKLEN-2; i++) {
    unsigned start = code[i];
    unsigned end = code[i+JUNKLEN+1];
    unsigned reg = start-PUSH;

    if (start < PUSH || start >= PUSH+8) continue;   JUNK;
    if (end != POP+reg) continue;                    JUNK;

Если регистр окажется ESP, мы можем с уверенностью пропустить его, потому что мы никогда не будем использовать ESP в нашем сгенерированном коде (операции стека на ESP требуют особого рассмотрения, что не стоит усилий).

    if (reg == 4) continue; /* register 4 is ESP */

Как только мы сопоставим вероятную комбинацию PUSH и POP, мы затем попытаемся прочитать инструкции между ними. Если мы успешно сопоставим длину ожидаемых байтов, мы считаем, что это совпадение, которое можно заменить.

    j = 0;                                           JUNK;
    while (inc = readinstruction(reg,i+1+j)) j += inc;
    if (j != JUNKLEN) continue;                      JUNK;

Затем мы выбираем один из 7 регистров в случайном порядке (как объяснялось выше, чем мы не рассматриваем ESP), и записываем операции PUSH и POP для этого регистра в обоих концах последовательности.

    reg = rand()%7;                                  JUNK;
    reg += (reg >= 4);
    code[i] = PUSH+reg;                              JUNK;
    code[i+JUNKLEN+1] = POP+reg;                     JUNK;

Тогда все, что нам нужно сделать, это заполнить пространство между ними, используя нашу функцию записи записи.

    space = JUNKLEN;
    j = 0;                                           JUNK;
    while (space) {
      inc = writeinstruction(reg,i+1+j,space);       JUNK;
      j += inc;
      space -= inc;                                  JUNK;
    }

И здесь, где мы показываем смещение, которое мы просто заплатят.

    printf("%d\n",i);                                JUNK;
  }
}                                                                             

Наконец, мы имеем основную функцию. Это просто вызывает ранее описанные функции. Мы читаем в коде, заменим мусор, а затем запишем его снова. Аргумент argv[0] содержит имя файла приложения.

int main(int argc, char* argv[]) {

  readcode(argv[0]);     JUNK;
  replacejunk();         JUNK;
  writecode(argv[0]);    JUNK;

  return 0;
}

И все, что там есть.

Некоторые заключительные заметки

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

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

В любом случае, я надеюсь, что мои объяснения были достаточно ясными, и это был тот пример, который вы искали. Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать в комментариях.

Бонусное обновление

В качестве бонуса, я думал, что я бы также включил пример метаморфического кода на языке сценариев. Это сильно отличается от примера C, так как в этом случае нам нужно изменить исходный код, а не бинарный исполняемый файл, что немного легче.

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

Условия и структуры циклов немного сложнее, но их просто нужно переписать в виде условия, которое перескакивает на одну из двух разных меток. Я включил маркеры комментариев в код, где циклы должны были бы попытаться сделать это проще.

Пример кода на ideone.com

Все, что делает код, это эхо перетасованная копия самой, поэтому вы можете легко протестировать ее на ideone, просто вырезая и вставив результат обратно в исходное поле и запустив его снова.

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

Ответ 2

Публично доступные образцы метаморфического кода ограничены несколькими факторами:

  • 1) Экспертиза: Метаморфическое кодирование - чрезвычайно сложная техника в программировании. Количество программистов, способных кодировать когерентный и чистый метаморфический код, подходящий для выборки, - очень небольшое число.

  • 2) Финансовые стимулы: Метаморфическое кодирование ограничено в коммерческом применении. Из-за этого число программистов, обладающих достаточным навыком создания метаморфического кода, не имеют профессионального воздействия/стимула для создания/изучения методов метаморфического кодирования.

  • 3) Legitamicy: Метаморфическое кодирование имеет большие приложения при мощном создании вируса. Следовательно, любой ответственный специалист, создавший метаморфический код, будет иметь этические проблемы, свободно распространяющие образцы, поскольку хакер хакеров может использовать код для улучшения вредоносной атаки. И наоборот, любой хакер, который был достаточно компетентен для создания метаморфического кода, не имел бы стимула рекламировать свое умение, если бы одна из его атак была раскрыта, поскольку он тогда будет на очень коротком списке подозреваемых, основанных на компетенции.

  • 4) Секретность: Наконец, и, вероятно, наиболее реалистичным метаморфозным кодом, который так сложно найти, является то, что любой программист, который демонстрирует компетентность в метаморфическом программировании и не воспринимается властями для кибер-преступления, скорее всего, будут набираться правительственным агентством безопасности, частной охранной фирмой или антивирусной компанией, а последующие исследования/знания программиста затем подлежат соглашению о неразглашении, чтобы поддерживать конкурентное преимущество.

    /li >

Почему только C/C++ примеры?

Вы упомянули только примеры кода C/C++ для поли/метаморфического программирования и предположили, что только языки, близкие к аппаратным средствам, могут быть poly/metamorphic. Это справедливо для самых строгих определений поли/метаморфического кода. Интерпретированные языки могут иметь политическое/метаморфическое поведение, но полагаться на интерпретатор с установленным статичным исполнением, поэтому большая часть "подписи времени выполнения" не изменена. Только скомпилированные языки низкого уровня предлагают гибкость вычислений, чтобы иметь очень изменчивую "подпись времени выполнения".

Вот несколько "полиморфных" PHP-кода, которые я написал. PHP, являющийся интерпретируемым языком, а не скомпилированный язык, делает невозможным подлинный полиморфизм.

Код PHP:

<?php
// Programs functional Execution Section
system("echo Hello World!!\\n");
// mutable malicious payload goes here (if you were devious)

// Programs Polymorphic Engine Section
recombinate();
?>
<?php

function recombinate() {
  $file      = __FILE__;                    //assigns file path to $file using magic constant
  $contents  = file_get_contents($file);    //gets file contents as string
  $fileLines = explode("\n", $contents);    //splits into file lines as string array
  $varLine   = $fileLines[2];               //extracts third file line as string
  $charArr   = str_split($varLine);         //splits third line into char array
  $augStr    = augmentStr($charArr);        //recursively augments char array
  $newLine   = implode("",$augStr);         //rebuilds char array into string
  $fileLines[2] = $newLine;                 //inserts string back into array
  $newContents  = implode("\n",$fileLines); //rebuilds array into single string
  file_put_contents($file,$newContents);    //writes out augmented file
  sleep(1);                                 //let the CPU rest
  $pid = pcntl_fork();                      //forks process
  if($pid) {                                //if in parent:
    exit(0);                                //exit parent process
  }                                         //WARNING: creates 'Zombie' child process
  else {                                    //else in child process
    system("nohup php -f " .$file . " 2> /dev/null"); //executes augmented file
    exit(0);                                //exits exit child process
  }
}

function augmentStr($inArr) {
  if (mt_rand(0,6) < 5) {               //determines mutability
    /*$startIndex & $endIndex define mutable parts of file line as Xs
     * system("echo XXXXX ... XXXXX\\n");
     * 01234567890123            -7654321
     */
    $startIndex  = 13;
    $endIndex    = count($inArr)-7;
    $targetIndex = mt_rand($startIndex,$endIndex);     //choose mutable index
    $inArr[$targetIndex] = getSafeChar(mt_rand(0,62)); //mutate index
    $inArr = augmentStr($inArr);               //recurse
  }
  return $inArr;
}

function getSafeChar($inNum) {      //cannot use escaped characters
  $outChar;                 //must be a standard PHP char
       if ($inNum >=  0 && $inNum <= 9 ) { $outChar = chr($inNum + 48); }
  else if ($inNum >= 10 && $inNum <= 35) { $outChar = chr($inNum + 55); }
  else if ($inNum >= 36 && $inNum <= 61) { $outChar = chr($inNum + 61); }
  else if ($inNum == 62)                 { $outChar = " ";              }
  else                                   { $outChar = " ";              }
  return $outChar;
}

?>

ПРЕДУПРЕЖДЕНИЕ: Создает процесс зомби, умеет убивать процесс зомби перед запуском кода

Методы поиска информации:

В этой статье содержится более конкретная информация, чем Wikipedia. Однако эта статья не содержит истинного исходного кода. Если вы хотите мой совет, хотя маловероятно, что он найдет образец исходного кода, вы сможете найти достаточную академическую документацию, чтобы создать свой собственный метаморфический код. Подумайте об этом, чтобы начать (google scientar):

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

Удачи вам в поисках знаний!

Ответ 3

Этот ответ еще не закончен, я буду продолжать расширять его с течением времени, пока этот вопрос не будет завершен.

Пример сценария - PHP

Я сделал свою собственную копию PHP script, предоставленную Джеймсом Холдернессом, чтобы я мог убедиться для себя через демонстрацию того, как может работать метаморфический script. Здесь написана полная запись кода; http://null.53bits.co.uk/index.php?page=php-goto-replicator

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

Я намеревался продлить действие Джеймсом Холдернессом PHP немного, в рабочий пример саморепликации и морфинга.

Это исходный PHP-код, с которым я столкнулся;

<?php goto a01;
a01: $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';    goto a02;
a02: $randomString = __DIR__."/";                                                       goto a03;
a03: $i = 0;                                                                            goto a04;
a04: if ($i < 10)                                                                       goto a05; else goto a07;
a05:   $randomString .= $characters[rand(0, strlen($characters) - 1)];                  goto a06;
a06:   $i++;                                                                            goto a04;
a07: $randomString .= ".php";                                                           goto a08;
a08: $ARGS=Array("-f",$randomString);                                                   goto a09;
a09: $handle_out = fopen("$randomString", "w");  goto l01;
l01: $filename = __FILE__;                       goto l02;
l02: $contents = file_get_contents($filename);   goto l03;
l03: $lines = explode("\n",$contents);           goto l04;
l04: $collection = array();                      goto l05;
l05: $pattern = '%^[^:]+:.*goto [^;]+;$%';       goto l06;
l06: $i = 0;                                     goto l07;
l07: if ($i < count($lines)-1)                   goto l08; else goto l23;
l08:   $line = $lines[$i];                       goto l09;
l09:   $line = trim($line);                      goto l10;
l10:   if (substr($line,0,2) != '//')            goto l11; else goto l22;
l11:     if (preg_match($pattern, $line) === 1)  goto l12; else goto l13;
l12:       $collection[] = $line;                goto l22;
l13:       shuffle($collection);                 goto l14;
l14:       $j = 0;                               goto l15;
l15:       if ($j < count($collection))          goto l16; else goto l19;
l16:         echo $collection[$j]."\n";          goto l17;
l17:         fwrite($handle_out, $collection[$j]."\n");    goto l18;
l18:         $j++;                               goto l15;
l19:       $collection = array();                goto l20;
l20:       fwrite($handle_out, $line."\n");      goto l21;
l21:       echo $line."\n";                      goto l22;
l22:   $i++;                                     goto l07;
l23: fclose($handle_out);                        goto f01;
f01: $pid = pcntl_fork();                        goto f02;
f02: if ($pid == -1)                             goto f03; else goto f04;
f03:   die("Could not fork a new child\n");      goto f03;
f04: if ($pid)                                   goto f05; else goto f06;
f05:   exit(0);                                  goto f05;
f06: $sid = posix_setsid();                      goto f07;
f07: if ($sid < 0)                               goto f08; else goto f09;
f08:   die("Child posix_setsid error\n");        goto f08;
f09: sleep(10);                                  goto f10;
f10: pcntl_exec(PHP_BINARY, $ARGS);
l24: exit(0);                                    goto l24;
?>