Меня всегда интересовало, а можно ли похитить сокет другой программы и использовать его в своих целях? Еще как можно!
Обход фаервола, установка скрытых соединений, чтение конфиденциальных данных – это лишь немногое, что можно сделать, обладая сокетом.
Метод перехвата прост и широко обсуждаем в интернете. Нам не потребуется глубоких знаний Windows, потому что все осуществимо из User Mode (ring 3), но для понимания основы знать просто необходимо. RTFM Джеффри Рихтер “Windows для профессионалов: создание эффективных Win32-приложений с учетом специфики 64-разрядной версии Windows”, глава 3
Метод заключатся в следующем
- Получить список описателей (хэндлов - handle) открытых нужным процессом
- Найти среди них сокеты
- Скопировать их в свой процесс
В листинге, представленном ниже, я использовал функцию ZwQuerySystemInformation (Native API)
NTSTATUS ZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
для получения списка открытых описателей, передав ей в качестве первого аргумента SystemHandleInformation
Для получения типа описателя функцию NtQueryObject (Native API)
NTSTATUS NtQueryObject(
HANDLE ObjectHandle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG Length,
PULONG ResultLength );
c параметром ObjectTypeInformation (нам нужны только ‘File’) и с параметром ObjectNameInformation для получения имени описателя (нам нужны сокеты ‘\Device\Afd’). Эти функции находятся в библиотеке ntdll.dll
Копировать описатель – сокет в наш процесс мы будем функцией DuplicateHandle
Листинг кода (VS2008 Win32->Console Project), демонстрирующий описанный выше метод для вывода всех открытых сокетов в системе.
#include "stdafx.h"
#include
#include // нужный заголовочек с полезными структурами
#include // структура NTSTATUS
// подключаем сокеты для использования функций преобразования IP адреса и порта
#include
#pragma comment(lib, "ws2_32.lib")
#define BUF_SIZE \
102400 // размер буффера под таблицу информации и имени описателя
// http://msdn.microsoft.com/en-us/library/aa492492.aspx эх, у меня нет DDK
typedef enum _POOL_TYPE {
NonPagedPool = 0,
PagedPool = 1,
NonPagedPoolMustSucceed = 2,
DontUseThisType = 3,
NonPagedPoolCacheAligned = 4,
PagedPoolCacheAligned = 5,
NonPagedPoolCacheAlignedMustS = 6,
MaxPoolType = 7,
NonPagedPoolSession = 32,
PagedPoolSession = 33,
NonPagedPoolMustSucceedSession = 34,
DontUseThisTypeSession = 35,
NonPagedPoolCacheAlignedSession = 36,
PagedPoolCacheAlignedSession = 37,
NonPagedPoolCacheAlignedMustSSession = 38
} POOL_TYPE;
// структура необходимая для получения имени описателя -
// NtQueryObject(ObjectNameInformation)
typedef struct _OBJECT_NAME_INFORMATION {
UNICODE_STRING Name;
WCHAR NameBuffer[0];
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
// структура необходимая для получения типа описателя -
// NtQueryObject(ObjectTypeInformation)
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING TypeName;
ULONG TotalNumberOfHandles;
ULONG TotalNumberOfObjects;
WCHAR Unused1[8];
ULONG HighWaterNumberOfHandles;
ULONG HighWaterNumberOfObjects;
WCHAR Unused2[8];
ACCESS_MASK InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ACCESS_MASK ValidAttributes;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
USHORT MaintainTypeList;
POOL_TYPE PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
// используется в NtQueryObject
typedef enum _OBJECT_INFORMATION_CLASS {
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllInformation,
ObjectDataInformation
} OBJECT_INFORMATION_CLASS,
*POBJECT_INFORMATION_CLASS;
// функции мы будем подключать динамически, поэтому их необохдимо описать :)
// ZwQuerySystemInformation
typedef NTSTATUS(CALLBACK *LPFNZwQuerySystemInformation)(
SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation,
ULONG SystemInformationLength, PULONG ReturnLength);
LPFNZwQuerySystemInformation ZwQuerySystemInformation;
// NtQueryObject
typedef NTSTATUS(CALLBACK *LPFNNtQueryObject)(
HANDLE ObjectHandle, OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation, ULONG Length, PULONG ResultLength);
LPFNNtQueryObject NtQueryObject;
#define SystemHandleInformation \
16 // недокументированый enum SYSTEM_INFORMATION_CLASS
// структура используемая в ZwQuerySystemInformation
typedef struct _SYSTEM_HANDLE_INFORMATION {
USHORT ProcessId;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
// Расширенная структура для получения информации о всех описателях
typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
ULONG NumberOfHandles;
SYSTEM_HANDLE_INFORMATION Information[1];
} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
int _tmain(int argc, _TCHAR *argv[]) {
DWORD ret;
NTSTATUS ss;
PUNICODE_STRING us;
POBJECT_TYPE_INFORMATION ot;
int i, ress, rem_port, loc_port;
char *remaddr, *locaddr;
sockaddr_in sockname, locname;
WSAData WSData;
HANDLE hProcess, ObjHandle, hh;
HINSTANCE hNTdll = LoadLibrary(L"Ntdll.dll");
if (!hNTdll) return 1;
// ищем функции
ZwQuerySystemInformation = (LPFNZwQuerySystemInformation)GetProcAddress(
hNTdll, "ZwQuerySystemInformation");
NtQueryObject = (LPFNNtQueryObject)GetProcAddress(hNTdll, "NtQueryObject");
WSAStartup(MAKEWORD(2, 2), &WSData); // стартуем winsock 2.2
printf("There are the following sockets opened on system:\n");
// Получение числа описателей в системе
DWORD buffer_size = 0;
SYSTEM_HANDLE_INFORMATION_EX temp_info;
ss = ZwQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)SystemHandleInformation, &temp_info,
sizeof(temp_info), &buffer_size);
// выделяем память под информацию о описателях и
SYSTEM_HANDLE_INFORMATION_EX *system_handles =
(SYSTEM_HANDLE_INFORMATION_EX *)malloc(
buffer_size); // если C++ можно и (new BYTE[buffer_size])
ss = ZwQuerySystemInformation(
(SYSTEM_INFORMATION_CLASS)SystemHandleInformation, system_handles,
buffer_size, &buffer_size);
// выделяем память под сруктурки
ot = (POBJECT_TYPE_INFORMATION)malloc(BUF_SIZE);
us = (PUNICODE_STRING)malloc(BUF_SIZE);
// информацию о всех описателях мы получили теперь пробежимся по ним...
for (i = 0; iNumberOfHandles; i++) {
/*
Тут можно вставить проверку на описатели определенного процесса,
PID которого можно получить кучей разных способов (см. ссылки в конце
статьи)
*/
hProcess = OpenProcess(
PROCESS_DUP_HANDLE, FALSE,
system_handles->Information[i]
.ProcessId); // открываем процесс с нужными правами доступа
if (hProcess != INVALID_HANDLE_VALUE) {
hh = (HANDLE)system_handles->Information[i]
.Handle; // у меня были проблемы с преобразованиями - для этого
// и завел новую переменную
// теперь копируем описатель в адресное пространство своего процесса
if (DuplicateHandle(hProcess, hh, INVALID_HANDLE_VALU E, &ObjHandle,
DUPLICATE_SAME_ACCESS, 0, 0)) {
// вытаскиваем информацию о типе описателя
ss =
NtQueryObject(ObjHandle, ObjectTypeInformation, ot, BUF_SIZE, &ret);
if (ss == STATUS_SUCCESS) {
// кстати таким образом можно перехватывать любые обьекты ядра заданые
// в таблице описателей процесса,
if (lstrcmp(ot->TypeName.Buffer, L"File") ==
0) { // но нам нужен только File
// вытаскиваем информацию о имени описателя
ss = NtQueryObject(ObjHandle, ObjectNameInformation, us, BUF_SIZE,
&ret);
if (ss == STATUS_SUCCESS) {
if (lstrcmp(us->Buffer, L"\\Device\\Afd") ==
0) { // если это сокет
/*
==
=== Вот впринципе и все! мы нашли
сокет и он уже находится во власти нашего процесса теперь мы
можем делать с ним все, что захотим! ;) а я хочу вывысти
информацию о нем и о том кому он пренадлежит.
==
===
*/
ress = sizeof(sockaddr_in); //--- commenting this line would
//cause an 10014 error.
getpeername((SOCKET)ObjHandle, (sockaddr *)&sockname, &ress);
//--- определяем локальный IP и порт
ress = sizeof(sockaddr_in); //--- commenting this line would
//cause an 10014 error.
getsockname((SOCKET)ObjHandle, (sockaddr *)&locname, &ress);
//--- коевертируем в понятные для глаза данные )
remaddr = inet_ntoa(sockname.sin_addr);
rem_port = ntohs(sockname.sin_port);
locaddr = inet_ntoa(locname.sin_addr);
loc_port = ntohs(locname.sin_port);
// выводим информацию
wprintf(L"PID=%d; Local=%S:%d; Peer=%S:%d;\n",
system_handles->Information[i].ProcessId, locaddr,
loc_port, remaddr, rem_port);
}
} // --- name handle
}
} // --- type handle
} // -- duplicate handle
CloseHandle(hProcess);
}
} // --- for
// освобождаем память
free(ot);
free(us);
free(system_handles);
WSACleanup(); // отключаемс сокеты
FreeLibrary(hNTdll); // и выгружаем DLL
getchar(); // а это так для паузы )
return 0;
}
Полезные ссылки
Множество примеров использования Tool Help и ZwQuerySystemInformation
Недокументированные функции Windows NT/2K/XP/2003