
Постоянный пользователь
    
Группа: Новички
Сообщений: 520
Регистрация: 16-June 06
Пользователь №: 431
Заходит на форум с гостевика.

|
Из некоторых нетривиальных приёмов, которые лично я использовал в своих программах
1. Повторный вызов программой самой себя. В том числе самомодификация, самоперемещение...
Сначала надо осознать себя. Чтобы было ясно, что нужно перемещать, модифицировать и перезапускать.
Как известно, пока программа работает (выполняется), её EXEшный файл "открыт" в режиме, не допускающем повторное открытие на запись, дозапись, перезапись (т.е. любую модификацию), а так же удалить этот файл. Однако его можно открыть для чтения и скопировать в другое место (либо под другим именем). При этом доступность этого файла определяется режимом открытия, и никак не зависит от файловых атрибутов (либо только дополняется ими). Итак, мы можем
1.1. Получить имя исполняемого файла самой этой программы (которая сейчас выполняется). В Win32 API это надёжнее всего сделать функцией
GetModuleFileName((HMODULE)NULL,(char *)буфер,(unsigned int)длина_буфера); // Это на Си. Ну, на Паскале - аналогично, только там типы по-другому называются
Буфер имеется в виду под ASCIIZ-строку (такое представление строк характерно для языка Cи, в Паскале же они приделаны сбоку). Но все (большинство) WinAPIшных функций ориентированы на ASCIIZ-строки, и с этим придётся считаться. Впрочем, у какого-нибудь Бейсика доступ к WinAPI может быть сделан через свои врапперы, которые берут бэйсичные строки (что-то вроде типа Variant), преобразуют их в ASCIIZ, вызывают API и делают обратное преобразование... Тогда надо смотреть руководство по той языковой среде. Размер буфера должен быть не менее 260 байт (я обычно даю ему 264 - это число делится на 8, а сейчас компиляторами обычно (по умолчанию) используется выравнивание на QWORD - то есть, 8 байт, что будет особо актуально для грядущей 64-битной архитектуры). Предварительно буфер неплохо бы занулить, а для этой функции указать его длину не полную, а 260 байт. Пусть она думает, что он короче, и в конце гарантированно оставит несколько нулей на всякий случай. Хотя реально имя файла не превысит 256 байт. Ещё можно попробовать взять _argv[0] (на С/С++) или ParamStr(0) (на Паскале) - короче, "нулевой" параметр командной строки вызова. Но не во всех системах гарантируется, что там имя файла представлено с полным путём (смотря как программа вызвана). Например, когда я стартовал программу под отладчиком, я видел там имя файла без пути.
Если Вы не уверены, что имя файла - полное, то можно всегда попробовать вызвать API-функцию
GetFullPathName(имя_файла,длина_буфера,буфер,&указатель_на_имя);
Соображения насчёт буферов и указания их длин - аналогичные оговорённым выше. Последний параметр здесь - указатель на указатель (на ASCIIZ-строку), куда будет помещён указатель на начало той части полного пути (в нашем буфере), где будет начинаться просто имя файла без пути. В результате должен получиться полный (абсолютный) путь с именем файла - даже если имя_файла было задано без пути или с неполным (относительным) путём, либо там присутствовали ".", ".." и т.п. - всё это будет расшифровано и конкретизировано. Если имя_файла было задано уже с полным путём, то эта функция просто копирует его в буфер "как есть".
Далее его можно превратить в "короткое" имя вызовом API-функции
GetShortPathName(длинное_имя,буфер,длина_буфера);
Насчёт буферов и их размеров - всё аналогично. Эта функция преобразует имя файла вместе с путём (если он там указан) в "короткую" версию, совместимую с форматом 8.3 (т.е. совместимую с MS DOS). Эту "короткую" версию она помещает в буфер. Там заведомо не будет пробелов, всякой прочей бяки, и это касается не только имени файла, но и всех элементов пути. На самом деле, в файловой системе Win32 (FAT32, NTFS, CDFS...) с поддержкой длинных имён, у каждого "длинного" имени файла или папки есть свой "короткий" псевдоним (альтернативное имя), по которому этот же объект доступен не менее надёжно. Ну, может быть, не так красиво для человека... Если имя файла (и его путь) - уже "короткое" (удовлетворяет формату 8.3), то эта функция просто копирует его в буфер "как есть".
Из полученного имени текущего файла (преобразованного к полному пути в "короткой" форме 8.3) можно получить путь каталога (папки). Это, собственно, то место, откуда вызвана эта программа. Пригодится. Разбирать строку можно самому, а есть и стандартные (библиотечные) функции _splitpath, fnsplit, SplitName...
Далее для простоты везде предполагается, что мы имеем "короткую" версию имени файла и пути каталога. Это существенно, если мы хотим использовать их в BATниках для команд DOS, да и вообще не помешает (например, при вызове программы по длинному имени, содержащему пробелы, это длинное имя следует заключать в двойные кавычки, но в данном случае, когда имена у нас только короткие, с кавычками можно не заморачиваться).
Кстсти, все вышеперечисленные API-функции возвращают длину, на которую реально заполнен буфер. 0 должен пониматься как признак неудачи (типа, "ну, не шмогла я!"), и в этом случае можно продолжить использовать прежнюю форму имени, либо скопировать строку в буфер самому.
Впрочем, для каких-то целей (например, для копирования в другое место) можно составить самому полное имя с путём, взяв путь в "короткой" версии (т.е. полный, но преобразованный к формату 8.3), а имя файла - "длинное". Найти "длинное" имя (если оно есть) по "короткому" можно API-функцией FindFirstFile, подставив в качестве маски реальное имя файла (она его всегда найдёт, но если у файла есть "длинная" версия имени, то отдаст именно её, а если только короткая, то её).
1.2. Неплохо бы написать отдельную подпрограммку для гарантированного нахождения имени файла, которого ещё нет (т.е. имени под новый файл). Факт существования файла можно узнать, попытавшись опросить его атрибут. Этот способ надёжен вне завистмости от того, файл это, каталог, метка тома, видимый, невидимый и доступен ли сейчас для открытия... Для этой подпрограммки следует сделать параметры для указания расположения (пути каталога или папки) и общей маски имени. Например, может понадобиться найти новое (пока несуществующее) имя с конкретным расширением EXE или BAT. И в заданном каталоге (папке), а не обязательно в текущем месте... Лично я обычно веду счётчик, преобразую его к строке в 36-ричном исчислении (получаются всевозможные сочетания цифр и букв латинского алфавита), подставляю в маску и проверяю на существование. Если объект (не важно какой) с таким именем существует, то наращиваю счётчик, и опять...
Итак, ищем новое имя для пока несуществующего файла типа EXE или даже с произвольным расширением. Использовать жёстко заданные конкретные имена я тут не рекомендую. Копируем туда файл нашей программы в файл с этим именем. API-функция CopyFile может это сделать. Для неё не важно, что сам файл текущей программы сейчас выполняется, т.к. она открывает его только на чтение. С новой копией нашего файла мы можем делать всё что угодно. Имя его мы знаем (сами только что придумали), атрибут можем принудительно присвоить FA_ARCH (=32 или 0x20). Можно, например, открыть для чтения-записи и модифицировать в каком-то месте.
1.3. Ищем новое имя для пока ещё несуществующего файла типа .BAT, и создаём этот файл (открываем на запись с созданием). Это новый файл, и о нём никто не знает кроме нашей программы, которая сама его только что создала.
Пишем туда следующие команды. Просто как строки текста.
@echo off rem -- Сначала небольшие проверочки if not exist [копия_нашего_файла] goto out if not exist [наш_файл.EXE] goto dalee rem -- Наш файл, который мы хотим подменить и перезапустить, пока существует. Этого следовало ожидать :snova rem -- Эта переменная среды (Environment), которую мы используем для своих целей. Здесь мы пока очистим её. Хотя это без разницы set WAITDELAYVAR= rem -- Попробуем удалить наш файл, но пока он работает, он этого не даст сделать. Даст только когда завершит свою работу. del [наш_файл.EXE] rem -- И сразу проверим, как это удалось. Не будем ни начто полагаться. Если удалось, тогда продолжим по метке :dalee if not exist [наш_файл.EXE] goto dalee rem -- Иначе надо подождать, пока он не завершит своё выполнение. До этого он не даёт себя уничтожить. Организуем цикл с задержкой set WAITDELAYVAR=_ :waitagain rem -- Обеспечим достаточно много (ну, скажем, 80-100) циклов. Для батника это выльется в несколько миллисекунд задержки if %WAITDELAYVAR% == ________________________________________________________________________________ __________ goto snova rem -- Пока эта строка ещё не доросла до нужной длины, нарастим её ещё на один ундербар set WAITDELAYVAR=%WAITDELAYVAR%_ goto waitagain :dalee rem -- Здесь наш файл.EXE уже уничтожен, и проверено, что его нет rename [копия_нашего_файла] "[НАШ_ФАЙЛ.EXE]" rem -- Либо можно через Copy-Del. А можно проверить, выполнилась ли команда rename на самом деле? if exist [наш_файл.EXE] goto call_me rem -- А вдруг переименование не удалось? Камаднда rename - она капризная. Например, не работает между разными устройствами if not exist [копия_нашего_файла] goto out copy /Y [копия_нашего_файла] "[НАШ_ФАЙЛ.EXE]" del [копия_нашего_файла] :call_me rem -- Повторный вызов нашего файла [наш_файл.EXE] параметры вызова... :out rem -- Самоубийство этого батника - вместо %0 можно вписать его реальное имя.BAT, которое мы знаем, так как сами его создавали del %0
Здесь вместо [копия_нашего_файла] и [наш_файл.EXE] надо подставить конкретные имена файлов, которые мы знаем (сами придумали). В последней строке, которая предписывает этому батнику сделать харакири, можно также подставить его конкретное имя, но можно %0.
Коль скоро мы сами создаём этот файл (пишем программой, а не вручную), то подстановку имён файлов организовать нетрудно. Можно использовать стандартную функцию fprintf(файл,"\ @echo off\n\ if not exist %s goto out\n\ if not exist %s goto dalee\n\ ... del %%0\n","имя_копии_файла","наш_файл.EXE",...);
Как ни странно, батные файлы позволяют так делать - совершать харакири (я проверял это и под DOS, и под 95-98, и под NT-2000-XP). Ах, да, где я писал "[НАШ_ФАЙЛ.EXE]" - в командах копирования и переименования, туда лучше подставить первоначальную (не преобразованную к "короткой" форме 8.3) версию имени файла программы, но заключить его в двойные кавычки. Command.com от Windows-95,98,ME и CMD.EXE от Windows-NT,2000,XP это понимает правильно, и умеет работать с длинными именами. Тогда будет сохранено длинное имя файла, как оно было раньше. Двойные кавычки не повредят и для короткого имени.
Итак, создав такой батник, запускаем его на исполнение
В стандантной библиотеке Си (если пишете на этом языке) есть функция spawnl, которую можно вызвать
#include <process.h> ... spawnl(P_NOWAIT,"имя_файла.BAT","имя_файла.BAT",NULL);
Аналогичные средства должны быть в других языках, но я не помню, где, например, в Паскале задаётся параметр аналогичный используемым здесь P_WAIT или P_NOWAIT. Здесь константа P_NOWAIT в качестве первого параметра предписывает запустить процесс асинхронно. В MS DOS это не работало. В других языках - свои аналоги, но общая Win32 API позволяет использовать такие вызовы
// Подготовка: надо иметь структуры данных, описанные в Win32 API STARTUPINFO Startup; // Структурка, которую мы заполним сами и передадим по указателю для CreateProcess PROCESS_INFORMATION ProcInfo; // Структурка, которую нам заполнит CreateProcess // Подготовка этих структурок memset(&Startup,0,sizeof(Startup)); // Сначала занулим её memset(&ProcInfo,0,sizeof(ProcInfo)); // и эту тоже Startup.cb=sizeof(Startup); // Это поле играет роль сигнатуры, и должно содержать длину этой структуры в текущей версии API Startup.dwFlags=STARTF_USESHOWWINDOW; // Поле флагов. Здесь мы укажем только один флаг, что будем использовать только поле wShowWindow Startup.wShowWindow=SW_HIDE; // А вот и оно. Укажем, что окно (консоль) должно быть скрыто совсем. Другие поля не используются.
CreateProcess(NULL,"имя_файла.BAT",NULL,NULL,FALSE,DETACHED_PROCESS|NORMAL_PRIORITY_CLASS,NULL,NULL,&Startup,&ProcInfo);
и тут же завершаем работу; Exit(0);
Но можно вызвать более простые и высокоуровневые функции ShellExecute или ShellExecuteEx. Например,
ShellExecute((HWND)NULL,"open","имя_файла.BAT",NULL,NULL,SW_HIDE);
Оба этих вызова обеспечивают АСИНХРОННЫЙ запуск нового процесса. Это значит, можно не дожидаться его завершения. Если хочешь сделать синхронный вызов (с ожиданием завершения и получением результата - кода выхода или ошибки), то в Win32 придётся вызвать функцию GreateProcess или ShellExecuteEx, там получить хендл процесса (у CreateProcess он помещается в одно из полей структурки ProcInfo, у ShellExecuteEx - аналогично, но там всё передаётся в структурке типа SHELLEXECUTEINFO, передаваемой по указателю - там и входные и выходные данные). Получив этот хендл, можно периодически проверять состояние этого процесса и управлять им (например, убить вызовом TerminateProcess). Проверять можно обращением к API-функции GetProcessExitCode для этого хендла, и пока она возвращает значение STILL_ACTIVE, то процесс пока ещё работает (иначе она отдаст код выхода - обычно 0 или код ошибки). Пока ждёшь, можно обрабатывать свои оконные сообщения и другие события... Короче, не скучать. Но можно и оформить свою подпрограмму, которая сама гоняет цикл ожидания, там выполняет Sleep чтобы экономить процессорное время... Именно так реализована стандартная библиотечная функция С/С++ spawnХХХ, вызываемая с параметром P_WAIT (под MS-DOS параметр P_WAIT был как бы всегда, P_NOWAIT игнорировадся) поступает имено таким образом. То есть, стартует процесс асинхронно и ждёт сама, выполняя при этом обработку оконных событий по умолчанию. Но там слишком примитивно. Самому можно круче заделать. Тайм-ауты там всякие и всё такое прочее.
Но нам тут нужен именно асинхронный запуск процесса, которому мы можем что-то завещать. А контролировать его мы не будем, т.к. сами мрём. В данном случае рассмотрен самоперезапуск программы после самомодификации (вернее, модификации своей копии). Но можно самоперезапускаться в других случаях - Перенести себя в другое место. Например, в системную папку C:\WINDOWS\SYSTEM32\ и снова запустить уже оттуда - Вызвать себя с другими параметрами командной строки - просто перевызвать себя (без уничтожения и переименования). Тогда батник получится намного проще. И всё же, неплохо бы дать задержку, хотя бы вслепую (ну, хотя бы один цикл, как показано выше), чтобы программа могла завершить свои дела и не пересекаться со своей новой инкарнацией.
2. Неубиваемые программы.
Пусть у нас реализован (как подпрограмма) самоперезапуск. Даже проще, без копирования, переименования и переноса, а просто перезапуск этого же EXEшника через BATник, который создаётся перед "смертью" процесса. Тот батник потом тоже делает себе харакири, выполнив свою миссию. И не оставляет никаких следов.
Итак, мы можем - Ловить сообщения Windows. Например, в том цикле, где у нас крутится GetMessage или PeekMessage. Среди них есть собщения WM_CLOSE для Главного окна и WM_QUIT - то есть, приказ умереть Бывает ещё WM_SHUTDOWN, но это уже бесполезно для нас. Надо всё же позволять выключить компьютер. - Обрабатывать события в понятиях объектов и компонентов. Например, ловить событие OnDestroy для главной экранной формы (это кто использует VCL и пишет на Delphi или Borland C++ Builder). Можно использовать деструктор класса, и иметь глобальный объект этого класса со статическим размещением. В нормальных объектно-ориентированных системах он должен быть вызван при завершении. - Навтыкать это во всех местах, но во избежание конфликтов между этими местами, завести себе глобальную переменную, по которой смотреть, надо ли это делать, и отмечать, что "сделано".
И во всех этих случаях делать самоперезапуск. Тогда даже если мы снимаем эту задачу через Ctrl-Alt-Del или Менеджер Процессов, но, конечно, при условии, что сама она не подвисла, Windows (или его Менеджер задач) сначала посылает процессу сообщение WM_CLOSE или WM_QUIT. То есть, даёт ей шанс самой корректно завершиться. И только если она не отвечает в течение какого-то времени и не выполняет приказ "сдохни", то Менеджер Задач грубо обрывает её API-функцией TerminateProcess... Но сначала всё же пытается по-хорошему. Дело в том что TerminateProcess может приводить к утечке некоторых ресурсов, и вообще, не рекомендуется для использования (она как пожарный шланг, не для постоянного употребления). Или, может быть, даже сама APIшная реализация TerminateProcess сначала пытается остановить процесс по-хорошему. Но в данном случае мы реагируем именно по-хорошему. То есть, умираем. Со стороны Windows к нам никаких претензий нет. Но мы косвенно (даже не сами сразу, а через батник) завещаем запустить заново свою новую инкарнацию. А это уже - другой процесс. Результатом будет "неубиваемый" процесс.
Разумеется, пока EXEшный файл работает, никто не может его удалить, перезаписать... Но для батника, которому эта программа завещала свой труп, это не проблема. Для пущей надёги его можно самопереместить в системную папку C:\Windows\System32, сделать ReadOnly, Hidden, System... Тогда ни у кого рука не поднимется с ним что-то делать.
3. Автозапуск
А теперь как обеспечить его автозапуск. Есть много способов. Есть папка Autorun (Автозапуск). Есть файл WIN.INI (по крайней мере, в Windows-95,98,ME). Но большинство программ автозапускаются по реестру.
Лезем в реестр. Там есть несколько разделов HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUN HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUNONCE HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUNONCEEX HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUNSERVICES HKEY_LOCAL_MACHINE\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUNSERVICESONCE HKEY_CURRENT_USER\SOFTWARE\MICROSOFT\WINDOWS\CURRENTVERSION\RUN и т.д. В каждом из них перечисляются все программы, которые надлежит стартовать при входе в систему. Из HKEY_LOCAL_MACHINE автозапуску подлежат программы для всех юзеров. Из HKEY_CURRENT_USER - только для этого юзера. В HKEY_USERS перечислены все пользователи, но один из них (который сейчас текущий) отображается дважды: там и в HKEY_CURENT_USER. Сервисы запускаются раньше входа в систему любого юзера, и им даются все полномочия администратора (доступ к папкам, право на запуск программ и т.п.). В каждо таком разделе реестра параметры выглядят так:
имя=командная строка вызова программы возможно с параметрами
причём, имена там не имеют никакого значения, важны только командные строки. Ну, имена не повторяются... Себя там можно назвать как хочешь. По имени можно искать свою собственную запись.
Для работы со статьями и параметрами реестра есть соответствующие API-функции.
Тут мы можем 3.1. Занести туда запись о себе. То есть, просто командную строку вызова с параметрами. И даже как-нибудь красиво себя там назвать, чтобы это было похоже на какую-нибудь системную задачу. Чтобы не только средний ламер, но и системный админ отнёсся к ней с почтением. 3.2. С некоторой периодичностью (скажем, раз в секунду или в 10 секунд) проверять там наличие этой записи о себе, и восстанавливать её, если она там вдруг исчезла. Почему исчезла? Ну, мало ли?... 3.3. Обязательно делать это в начале своей работы и при отработке автоперезапуска перед смертью.
Можно даже поступить ещё хитрее. Взять какую-нибудь системную задачу, которая входит в состав Windows, но без неё можно обойтись, и вымарать её из раздела автозапуска, и перезагрузить систему (или отложить эту пакость, пока юзер сам не завершит работу). На следующем сеансе Windows, когда та системая задача не будет автозапущена, уничтожить или переименовать (перенести в другое место) её EXEшный файл, а самого себя скопировать на её место под её именем, и самоперезапуститься оттуда. Ну, там, внестьсь в реестр для автозапуска и сидеть там. И никто ничего не заподозрит. Поможет только переустановка виндов. И то, не всегда. Можно сидеть сразу в нескольких местах, проверять наличие себя везде, и автоматически восстанавливать, если где-то что-то исчезнет.
Тогда никакая сволочь не сможет вымарать тебя оттуда, даже если напрямую полезет в реестр. То есть, вымарает, и даже сама для себя убедится, что тебя там больше нету, но через какое-то время (считанные миллисекунды) ты там снова возникнешь. А эта сволочь (и даже живой юзер через RegEdit, RegCleaner или что там у него есть ещё) этого не увилит (если только не догадается сделать Refresh). Но если даже и догадается, то пусть беснуется в бессильной злобе...
Ну, короче, для среднего ламера эта проблема непреодолима. Даже если он знает, что такое реестр и менеджер задач... Ах, да, и никакой антивирь тебя не поймает, поскольку нет ничего криминального в твоих действиях (в каждом по отдельности). Все взрослые программы делают это.
4. Про сетевых шпионов.
А что такое "открытый порт"? Это когда какая-то сетевая служба, установленная на твоём компе, готова принять и обработать какие-то данные, присылаемые на твой адрес? Тогда проще просто отключить наф сами эти службы... Наф тебе сервер на домашнем компе? Да и вообще, стандартные сетевые службы мало чего дадут для настоящего шпиона. Там не так уж и много "дыр", а те постоянно латаются.
Но твоя программа, которая такая живучая и неубиваемая, может делать всё что хочет. Например, используя WinSock (Windows Sockets) создать входящий сокет по протоколу TCP/IP и ждать соединение по нему. Дождавшись, она может получить новый (входящий) сокет (то есть, входящее соединение) и создать отдельный поток (CreateThread), который будет вести диалог по этому соединению. То есть, выступать как сервер. По какому протоколу - как сам его реализуешь. Хоть по своему собственному. Номер порта там - какой укажешь (если только он не занят уже). Можно даже захватывать порты в стандартном диапазоне номеров. Или перебирать, ища первый свободный.
А можно сделать наоборот. Пускай твоя программа сама устанавливает исходящее соединение (то есть, как бы Клиент) с каким-то заранее известным Сервером, который находится где-то в Интернете, и докладывает туда как Алекс Юстасу... Или спрашивает у него указаний и исполняет их от своего имени на этом компьютере... Протокол общения может быть какой угодно. Хоть обмен текстовыми командами, вопросами и ответами. Или твоя программа может сама назваться хоть "интернет-эксплорером". И никакой файрволл его не остановит. И сервер, и клиент ты пишешь сам, и можешь не оглядываться ни на какие стандартные протоколы передачи данных (которые ещё дубовее, чем можешь придумать ты сам). А ещё лучше чем TCP/IP использовать дейтаграммы UDP или просто пакеты Simple IP. Надёжность тогда придётся отслеживать самому. То есть, организовывать внутреннюю нумерацию пакетов (или дейтаграмм), контроль, подтверждения, таймауты, повторы, целостность... Зато никакая зараза не узнает, что ты по каким-то своим, одному тебе ведомым понятиям, установил некое подобие виртуального соединения с каким-то особым, одному тебе ведомым сервером, и действуешь по заданию Центра. С точки зрения системы ты просто обмениваешься какими-то низкоуровневыми пакетами... Эти пакеты можно даже маскировать под какой-нибудь безобидный протокол, ну, скажем, запрос на получение точного времени... Если твой собственный сервер тоже знает обо всех этих условностях для маскировки, то ему это будет поф (только трафик будет чуть побольше). А о чём вы на самом деле... Ну, это дело можно и зашифровать. Алгоритмы шифрования и расшифровки рассматриваются в разных книжках, там есть и примеры...
А запросы к серверу и ответы могут быть такими: - Здравствуй, господин Сервер! Это я, твой Клиент, с такого-то IP-адреса! Чё мне тут терерь делать? - Давай сюда все адреса, телефоны, пароли, явки! - Пожалуйста, о мой Господин! - далее следуют адреса, телефоны, пароли и явки. - А ну-ка, Что там у тебя в такой-то статье реестра? - То-то и то-то, о мой Господин! - А засади-ка туда теперь вот это! - Слушаюсь и повинуюсь, мой Господин! - А теперь скачай из Интернета (вот тебе УРЛ) такой-то вирус! - Есть! - И запусити его на выполнение! - Слушаюсь! - А теперь выключи компьютер, сожги процессор, отформатируй диск, выжги монитор и вообще взорви всё нах! - Бубух!
Кто тут кому сервер, а кто клиент - это уж они сами меж собой разберутся. А в заголовках пакетов пускай значится безобидный протокол, хоть бы и HTTP, и номера портов все - стандартные... Ну, если кто-то другой сдуру обратится к тому "серверу", то ему можно даже не отвечать... Мало ли в Инете таких битых серверов, которые не отвечают?
5. Как сделать программу невидимой.
Это проще всего. Просто надо НЕ ДЕЛАТЬ чего-то такого, что делает её видимой. Например, сделать её как GUI- (а не консольное) приложение и использовать WinMain вместо main (это если на C/C++, разумеется), но там НЕ создавать никакого окна, НE регистрировать оконный класс, и вообще, не делать ничего, связанного с окнами. И НЕ пользоваться тем, что предлагает автоматизированная среда разработки. Убрать нафиг Главную экранную форму (как объект, если его автоматически предлагает создать среда разработки типа Borland C++ Builder), не создавать её и не инициализировать. А вместо этого самому (даже в WinMain'е) организовать цикл PeekMessage и обрабатывать их. Нас интересует только WM_QUIT, остальные, - как рекомендуется, обрабатывать по умолчанию.
Но можно сделать и окно. Но установить его в режим отображения SW_HIDE. Обычно это всё делается не через API, а методами объектно-ориентированных оболочек, таких как MFC, VCL и иже с ними. Но суть - та же самая. Окно - есть. Но его не видно. Потому что оно HIDE. Такая программа будет вести себя как резидентная (TSR) программа в старые добрые DOSовские времена. Но у Windows даже в минимальной конфигурации выполняются несколько десятков таких невидимых процессов.
Ах, да, чтобы такая программа не жрала как собака процессорное время, то когда ей нечем себя занять, пускай вызывает Sleep(0), Это такая APIшная функция. Можно программу "уложить спать" на заданное количество миллисекунд, но даже если на 0 (это её параметр), то она просто отдаёт системе квант времени, выделенного ей, не дожидаясь когда система отберёт его сама. Скажем, квант времени составляет 1 мс (это я к примеру, чисто условно). Он выделен этому процессу. Положим, это одноядерный процессор с тактовой частотой 3 ГГц. Будучи суперскалярным, он может за это время выполнить 1-6 млн. машинных команд (смотря какие там команды ему попадутся, а так же параллельно задействовать FPU, MMU...). Но выполнив небольшую проверку (например, всего за 1000 команд - сотня строк исходного кода) он понимает, что делать пока нечего. Это в 1-6 тыс. раз меньше, чем он мог бы выполнить за свою честную миллисекунду (фактически у него ушла на все эти проверки доля микросекунды). Тогда он делает Sleep(0), и отдаёт этот квант времени, использовав сотые доли процента от него. И в результате, "использование процессорного времени" этой задачей составит сотые доли процента. Если посмотреть Менеджером задач, то почти все процессы ведут себя так Использование процессора почти у всех 0% (ну, много меньше 1%). Впрочем, вызов Sleep(0) заделан и в GetMessage, и в PeekMessage... Так что, если крутится этот цикл, то можно не волноваться.
6. Как внедриться в систему
Тут важно чтобы эта прога была запущена хотя бы первый раз. Дальше уж она сама так вцепится, что и с мясом не вырвешь. Если мы её сами так напишем. А уж мы-то умеем!... Вся сложность в том, как заставить юзера запустить её первый раз. Ну, можно предложить ему супер-программу, которая делает именно то, что ему давно уже надо. Это будет классический троян. Вирусы и червяки умеют внедряться сами. Проще всего это сделать через AutoRUN на CD-rom и на флешках. Обычно, когда суёшь флешку в USB, то Windows пытается "распознать" и запустить то, что там... Ну, это как во времена дискет. Тогда было много бутовских вирусов. Суёшь дискетку в дисковод, обращаешься к ней в первый раз, а система читает бут-сектор и исполняет машинный код, записанный там, хочешь ты того или не хочешь. А я у себя давно отключил автораспознавание флешек и CD-ROMов. Ну, короче, надёжного и универсального способа нет. Есть много мелких дыр, которые где-то заделаны, а где-то нет. Тогда надо ломиться во все сразу. Тогда есть шанс.
Можно написать ActiveX-элемент, и поместить его на HTML-страницу, и разместить на своём сайте. У многих юзеров в настройках разрешено использовать ActiveX. Но именно этого у него нет. Тогда ему предлагается его установить. А вдруг у него в настройках разрешено и это? А вдруг он согласится? Вот, тут-то ему и можно подсунуть нечто...
--------------------
Теперь всё, я сюда больше не приду. Никогда.
|