ZeroAccess plugin P2P или спецификация сетевого протокола как руководство для разработки
Общее
Представляет из себя простой P2P загрузчик. Реализован в ZeroAccess виде плагина. P2P сеть строится следующим образом. Каждый бот имеет свой список пар других ботов. И периодически обменивается с другими ботами своими актуальными IP. Кроме списка IP бот может получать сначала информацию о файле имеющуюся у другого бота, а потом сам файл. Все общение между ботами производится по UDP протоколу, лишь запрос информации и скачивание файлов TCP
Протокол
При старте бот начинает слушать порты UDP, TCP 16464
Введу некоторое обозначение:
Код:
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
Бот считывает список нод из файла, формат файла:
Код:
struct CFG_NODE{uint32 ip;
uint32 time;
};
Все сообщения имеют заголовок формат:
Код:
struct HEAD{uint32 crc32;
uint32 cmd;
uint32 hop;
uint32 data;
};
За хидером следуют данные зависящие от команды cmd. Команд всего 3и:
Код:
getL - запросить список нод
retL - отдать список нод
newL - публикация ноды
данные за хидером шифруются таким алгоритмом:
Код:
void encrypt(uint8 *data, int len_data){
uint32 *p = (uint32*)data;
uint32 count=len_data>>2;
uint32 key = 'ftp2';do{*p ^= key;
key = ROL(key, 1);
p++;
--count;
}while ( count );
}
Команда getL
Бот считывает ноды из файла и с периодом в 1с рассылает сообщение вида:
Код:
struct HEAD{uint32 crc32;
uint32 cmd; // 'getL'
uint32 hop; // количество прыжков 0
uint32 data; // сессия, случайное число sess
};
HEAD head;
Нода получает это сообщение и в ответ оправляет информацию о своих нодах и файлах retL. И если hop=0, то выполняет проверку на внешний IP бота отправителя. Для этого отправляет пакет:
Код:
struct HEAD{uint32 crc32;
uint32 cmd; // 'getL'
uint32 hop; // количество прыжков 1
uint32 data; // ip отправителя
};
HEAD head;
замете количество прыжков стало равно 1, это сообщение доходит в том случае если бот имеет внешний IP.
Команда retL
Отправляет в ответ на getL
Код:
struct HEAD{uint32 crc32;
uint32 cmd; // 'retL'
uint32 hop; // значение из входящего сообщения (0 или 1)
uint32 data; // sess из входящего пакета
};
struct BLOCK_NODE{uint32 ip;
uint32 time_delta;
};
struct BLOCK_SIGN{uint32 name_file; //имя в виде числа 32 бита
uint32 time;
uint32 size;
uint8 sign[128];
};
HEAD head;
uint32 count_node; //количество нод не более 16
BLOCK_NODE node[count_node];
uint32 count_sign; //количество файлов не более 16
BLOCK_SIGN sign[count_sign];
Ноды в BLOCK_NODE выбираются в зависимости от времени CFG_NODE.time. При этом ноды сначала упорядочиваются по убыванию. Самое большое значение CFG_NODE.time считается самым актуальным. Выбирается не более 16и самых актуальных нод. И выполняется формирование пакетов BLOCK_NODE, причем время рассчитывается так:
Определяется дельта для от текущей даты:
Код:
BLOCK_NODE.time_delta=now()-CFG_NODE.time
При приеме пакета retL время восстанавливается:
Код:
CFG_NODE.time =now()-BLOCK_NODE.time_delta
Новые ноды добавляются к имеющемуся списку, упорядочиваются, выбираются и сохраняются те которые не старше 32 дней.
Если во входящем пакете hop=1, то бот считает себя нодой с внешним IP (супернодой), и выполняет отправку пакета newL 16 нодам из своего конфига CFG_NODE
Команда newL
Формируется в ответ на retL с hop=1 или newL
Код:
struct HEAD{uint32 crc32;
uint32 cmd; // 'newL'
uint32 hop; // 8 прыжков или hop-1 из входящего
uint32 data; // ip отправителя
};
HEAD head;
Сообщение рассылается 16и нодам из конфига бота.
При получении ботами newL сообщения, извлекается HEAD.data (ip ноды), и проверяется наличие этого ip с имеющимся списком. Если такого ip не оказалось, то производится его добавление. И повторная рассылка 16и нодам с HEAD.hop - 1, так продолжается пока HEAD.hop не станет равным нулю или ip не будет известен всем нодам.
Загрузка файлов
В пакетах retL имеется количество count_sign и BLOCK_SIGN. Это информация о файлах которая имеется у бота. При получении такого пакета бот проверяет цифровую подпись BLOCK_SIGN.sign, затем смотрит есть ли у него имя файла BLOCK_SIGN.name_file, если такое имя имеется то выполняется проверка времени.
При выполнении всех этих условий выполняется tcp соединение, и отправляется пакет:
Код:
struct BLOCK_FILE{uint32 name_file;
uint32 time;
uint32 size;
};
BLOCK_FILE info;
В ответ приходит файл зашифрованный RC4 с ключом md5(info). Файл расшифровывается и проверяется цифровая подпись, которая берётся из ресурсов принятого файла. Если все прошло успешно, то файл сохраняется.
Дополнительная литература
Старая версия протокола p2p модуля, и самого ZeroAccess
hxxp://www.kindsight.net/sites/default/files/Kindsight_Malware_Analysis-ZeroAcess-Botnet-final.pdf
p.s
Хочу заметить что этот протокол рассчитан лишь на загрузку файлов на машины юзеров. Но его можно немного модифицировать, добавить код который сможет туннелировать трафик. В качестве одного из файлов раздавать список IP на которые будут стучаться боты. А те боты на которые происходит обращение будут переправлять траф на наш CC. В результате между ботами и СС, будет прокладка в виде тех же ботов.