Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги хакеры / Защита_от_взлома_сокеты,_эксплойты,_shell_код_Фостер_Дж_

.pdf
Скачиваний:
14
Добавлен:
19.04.2024
Размер:
3.68 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

-xcha

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Winsock 2.0 201

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-x cha

 

 

 

Указать имя библиотеки непосредственно в файле с расширением .c

df

 

e

 

 

 

 

 

 

n

 

 

 

 

èëè .cpp. Это наиболее простой и предпочтительный метод, особенно если вы хотите передать свой проект в общее пользование; Включить библиотеки в рабочее пространство проекта, но тогда пере-

давать свой код другим людям становится сложнее. Если вы скачали из сети программу, а она не компилируется, проверьте, все ли библиотеки указаны. Ниже приводятся подробные инструкции о том, как пользоваться обоими способами.

Компоновка с использованием Visual Studio 6.0

1.Нажмите Alt+F7 для входа в меню Project (Проект) и выберите пункт Settings (Параметры).

2.В диалоговом окне Project Settings (Параметры проекта) перейдите на вкладку Link (Компоновка) и далее укажите курсором на поле Object/ Library modules (Объектные файлы / Библиотечные модули). Введите имя библиотеки ws2_32.dll и нажмите OK.

3.Теперь ваша программа будет скомпонована с библиотекой ws2_32.dll (ñì. ðèñ. 4.1).

Рис. 4.1. Диалоговое окно Project Settings в Visual Studio

Задание компоновки в исходном коде

1.Поместите следующий код сразу под директивами #include: #pragma comment(lib, «ws2_32.lib»).

2.Теперь ваша программа должна быть скомпонована правильно.

Прежде чем начать использовать функции из Winsock 2 API, необходимо создать объект WSADATA, который осуществляет доступ к библиотеке

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

i

 

 

F

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

t

 

P

D

 

 

 

 

 

 

 

 

o

P

D

 

 

 

 

 

 

 

 

o

 

 

 

 

NOW!

r

 

 

 

 

NOW!

r

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

202 Глава 4. Сокеты на платформе Windows (Winsock)

 

 

 

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

m

w Click

 

 

 

 

 

 

o

w Click

 

 

 

 

 

 

o

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

.

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

df

 

 

 

 

e

 

 

 

p

df

 

 

 

 

e

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

g

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

 

n

 

 

 

 

 

 

 

 

-xcha

 

 

ws2_32.dll. В примере 4.1 этот объект обладает многими свойствами, из кото--x cha

 

 

 

 

 

рых нас будет интересовать только wVersion. Макрос MAKEWORD() представляет номер версии в стандартном формате; так, MAKEWORD(2, 0) соответствует версии 2.0.

Пример 4.1. Объект WSADATA

1 WSADATA wsaData;

2 WORD wVersionRequested;

3 wVersionRequested = MAKEWORD(2, 0);

4 WSAStartup(wVersionRequested, &wsaData);

5 if ( WSAStartup(wVersionRequested, &wsaData) < 0 )

6 {

7printf("Неправильная версия");

8 exit(1);

9 }

10 SOCKET MySocket;

11MySock = socket(AF_INET, SOCK_STREAM, 0);

12MySock = socket(AF_INET, SOCK_DGRAM, 0);

13struct hostent *target_ptr;

14target_ptr = gethostbyname( targetip );

15if( target_ptr = gethostbyname( targetip ) == NULL )

16{

17printf("Не удается разрешить имя.");

18exit(1);

19}

20struct sockaddr_in sock;

21memcpy(&sock.sin_addr.s_addr,target_ptr->h_addr,target_ptr->h_length);

22sock.sin_family = AF_INET;

23sock.sin_port = htons( port );

24connect (MySock, (struct sockaddr *)&sock, sizeof (sock) );

25if ( connect (MySock, (struct sockaddr *)&sock, sizeof (sock) ) )

26{

27printf("Не удается установить соединение.");

28exit(1);

29}

30char *recv_string = new char [MAX];

31int nret = 0;

32nret = recv( MySock, recv_string, MAX, 0 );

33if( (nret = recv( MySock, recv_string, MAX, 0 )) <= 0 )

34{

35printf("Не получено никаких данных.");

36exit(1);

37}

38char send_string [ ] = "\n\r Hello World \n\r\n\r";

39int nret = 0;

40nret = send( MySock, send_string, sizeof( send_string ) -1, 0 );

41if( (nret=send( MySock, send_string, sizeof(send_string)-1, 0)) <= 0 )

42{

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

 

 

 

e

 

 

 

 

d

 

 

xch43

 

 

 

 

 

 

 

f-

 

an

 

 

 

 

 

 

 

 

 

 

 

44

 

 

 

printf("Не удается отправить данные."); exit(1);

45}

46socketaddr_in serverInfo;

47serverInfo.sin_family = AF_INET;

48serverInfo.sin_addr.s_addr = INADDR_ANY;

49listen(MySock, 10);

50SOCKET NewSock;

51NewSock = accept(MySock, NULL, NULL);

52closesocket(MySock);

53WSACleanup();

Анализ

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Winsock 2.0 203

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

В строках 1–9 для инициализации Winsock 2 вызывается функция WSAStartup(), которая принимает два параметра: номер версии и объект WSADATA, который и надо инициализировать. В случае ошибки функция возвращает код. Чаще всего это случается, если запрошенный номер версии больше имеющегося на машине. Если же вы запрашиваете более раннюю версию, то функция завершается успешно.

В строках 10–12 создается и инициализируется сокет. Функции socket() передаются три параметра: адресное семейство, тип сокета и протокол. В этой книге мы имеем дело только с адресным семейством AF_INET. Тип сокета может быть SOCK_STREAM èëèSOCK_DGRAM.SOCK_STREAM говорит о том, что должно быть создано двунаправленное потоковое соединение, для семейства AF_INET это означает протокол TCP. Константа SOCK_DGRAM означает, что сокет не требует установки соединения, в случае семейства AF_INET будет выбран протокол UDP. Последний параметр говорит, какой протокол будет использоваться для передачи данных. Значение зависит от указанного адресного семейства. Скорее всего, вам никогда не придется задавать этот параметр явно, так что оставляйте его равным нулю.

В строках 13–19 готовится информация об адресе и номере порта для сокета. Можно указывать как IP-адрес, так и полностью определенное доменное имя хоста, которое еще предстоит разрешить. Преобразование доменного имени в форму, пригодную для конфигурирования сокета, производится с помощью структуры hostent и функции gethostbyname(), которая возвращает такую структуру. Ей передается строка, идентифицирующая удаленную машину. Это может быть IP-адрес в точечно-де- сятичной нотации, полностью определенное доменное имя, имя машины в локальной сети или любое другое имя, которое понимает программа nslookup. Функция gethostbyname() вернет NULL, если не сможет

разрешить указанное имя.

204 Глава 4. Сокеты на платформе Windows (Winsock)

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

В структуру struct hostent будет помещен двоичный IP-адрес. Его необхо--x cha

 

e

 

 

 

 

df

 

 

n

 

 

 

 

 

 

 

 

 

 

димо скопировать в структуру sockaddr_in. Сначала в строке 20 объявляется переменная sock этого типа, а затем sin_addr.s_addr копируется адрес из структуры, на которую указывает target_ptr (строка 21). Это делает функция memcpy(), которая работает аналогично strcpy(), только не со строками, а с блоками двоичных данных. Ей передаются три параметра: куда копировать, откуда копировать и сколько байтов копировать.

Переменная sock пока еще не вполне готова, в строках 22 и 23 задается адресное семейство и номер порта. Идентификатор адресного семейства (AF_INET) помещается в поле sin_family, а номер порта, соответствующий службе, с которой мы хотим соединиться, – в поле sin_port. Отметим, что значение в поле sin_port должно быть представлено

âсетевом порядке байтов, поскольку именно такой порядок принят

âсетях TCP/IP. Для преобразования целого числа из машинной формы

âсетевую применяется функция htons().

В строках 24–29 с помощью функции connect() устанавливается соединение. Эта функция принимает три параметра и возвращает код завершения операции. Первый параметр – это дескриптор сокета, в данном случае MySock. Второй – указатель на структуру, содержащую адресную информацию, то есть номер порта, IP-адрес и адресное семейство. Все это уже помещено в переменную sock. Последним параметром является длина второго параметра, для ее определения применяется встроенная в язык функция sizeof(). Если ошибок не произошло, connect() возвращает 0. Как и в случае с WSAStartup(), настоятельно рекомендуется проверять код возврата, чтобы быть уверенным, что соединение действительно установлено.

В строках 30–37 мы занимаемся приемом данных от удаленной машины. Для этого предназначена функция recv(), которой передается четыре параметра: дескриптор уже соединенного сокета (MySock), буфер,

âкоторый будут скопированы пришедшие данные, длина этого буфера и набор флагов, уточняющих порядок работы. В частности, флаг MSG_PEEK говорит, что нужно прочитать данные, но не удалять их из системного буфера. Другой флаг MSG_OOB используется совместно с протоколом DECnet. Обычно задается просто значение 0, тогда данные копируются в ваш буфер, а из системного удаляются. Функция возвращает число прочитанных и помещенных в буфер байтов. Отрицательное значение свидетельствует об ошибке, нуль – о том, что прочи- тан «конец файла», то есть удаленный компьютер закрыл свой конец соединения.

В строках 38–45 вызывается функция send() для отправки данных. Она тоже принимает четыре параметра и возвращает целое число. Первый

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Winsock 2.0 205

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

-x cha

 

 

 

параметр, как и в случае recv(), – это дескриптор сокета, второй – ука-

df

 

e

 

 

 

 

 

 

n

 

 

 

 

затель на буфер, содержащий отправляемые данные, третий – длина этого буфера. Обратите внимание, что мы вычитаем 1 из длины буфера, полученной с помощью sizeof(), чтобы не учитывать (и не передавать) завершающий нуль1. Последний параметр тоже содержит флаги. Функция возвращает число отправленных байтов, которое может быть

и меньше заданного третьим параметром2.*

В строках 46–49 приведен фрагмент кода, характерный для серверных приложений, которым нужен сокет, находящийся в режим ожидания запросов на соединение. Для перевода сокета в такой режим служит функция listen(), которая принимает два параметраи возвращает целое число. Но сначала сокет нужно создать, указав адрес локального компьютера и номер прослушиваемого порта. Запишите в поле sin_addr.s_addr значение INADDR_ANY, означающее, что вы готовы принимать запросы на соединение, поступающие на любой из сетевых интерфейсов компьютера. Первым параметром listen() является дескриптор созданного сокета, вторым – длина очереди ожидающих соединений.

В строках 50–51 вызывается функция accept(), которая будет ждать, пока не придет запрос от клиента. Она принимает три параметра и возвращает дескриптор нового сокета. Первый параметр – дескриптор сокета,

находящегося в режиме ожидания, второй – указатель на структуру, в которой будет возвращен адрес клиента, третий – длина второго параметра. Последние два параметра необязательны, вместо них можно передать NULL. Возвращенный функцией accept() сокет можно использовать для последующего обмена данными с клиентом.

В строках 52 и 53 производится очистка, важная операция, которой часто пренебрегают. В процессе очистки вызываются две функции: closesocket() è WSACleanup(). Первая закрывает сокет и освобождает все занятые им ресурсы, вторая освобождает память, выделенную объекту WSADATA, и выгружает библиотеку ws2_32.dll. По мере возрастания размера и сложности ваших программ очень важно не забывать своевременно уничтожать не нужные более объекты. Иначе приложение будет потреблять больше памяти, чем необходимо.

1 В данном случае проще было бы воспользоваться функцией strlen(), которая возвращает длину строки без завершающего нуля. Впрочем, strlen() вычисляется во время исполнения программы, а sizeof – во время компиляции, так что мы получаем хоть небольшой да выигрыш в производительности. (Прим. перев.)

2 Не столько отправленных, сколько скопированных в буфер сокета. Данные могут быть отправлены позднее. Если send() возвращает число, меньшее указанной длины данных, следует повторить вызов, сместив указатель на начало неотправленных данных и уменьшив длину остатка. (Прим. перев.)

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

206 Глава 4. Сокеты на платформе Windows (Winsock)

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

m

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

g

.c

 

 

.

 

 

 

 

g

.c

 

 

 

p

 

 

 

 

 

 

 

 

 

p

 

 

 

 

 

 

 

 

 

 

df

 

 

n

e

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

Пример: скачивание Web страницы

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

-x cha

 

 

 

 

 

с помощью WinSock

В этом примере мы построим простой робот для скачивания Web-страницы. Их содержимое будет выводиться на экран.

Программа ожидает, что в командной строке будет задано три аргумента: IP-адрес сервера, номер порта и имя файла. Обязательным является только IP-адрес. Если порт не задан, по умолчанию принимается значение 80, а в отсутствии имени файла скачивается начальная страница Web-сервера. Приложение должно отфильтровывать все страницы, содержащие сообщения об ошибках.

Пример 4.2. Простое приложение для скачивания Web*страниц

1 #include <stdio.h>

2 #include "hack.h"

3

4 int main(int argc, char *argv[])

5 {

6int port = 80;

7 char* targetip;

8

9 if (argc < 2)

10{

11printf("WebGrab usage:\r\n");

12printf(" %s <TargetIP> [port]\r\n", argv[0]);

13return(0);

14}

15

16 targetip = argv[1];

17 char* output;

18

19 if (argc >= 3)

20{

21port = atoi(argv[2]);

22}

23

24 if (argc >= 4)

25{

26output = get_http(targetip, port, argv[3]);

27}

28else

29{

30output = get_http(targetip, port, "/");

31}

32 if( is_string_in("Error 40", output ) ||

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

 

 

 

e

 

 

 

 

d

 

 

xch33

 

 

 

 

 

 

 

f-

 

an

 

 

 

 

 

 

 

 

 

 

 

34

 

 

 

 

 

 

 

 

 

 

35

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

F

 

 

 

 

 

 

t

 

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Программирование клиентских приложений 207

 

to

 

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

 

.

 

 

 

 

 

.c

 

 

 

 

p

 

 

 

 

g

 

 

is_string_in("302 Object moved", output ) ||

 

 

 

df-x chan

e

 

 

 

 

 

 

 

is_string_in("404 Not Found", output ) || is_string_in("404 Object Not Found", output ))

36{

37printf("Такой страницы нет!");

38}

39else

40{

41printf("%s", output);

42}

43return(0);

44}

Анализ

В строке включается файл hack.h, который будет представлен ниже в примере 4.5.

В строках 32–35 отфильтровываются различные сообщения Web-сер- вера об ошибках, возвращаемые, когда искомой страницы на сервере не оказалось.

Программирование клиентских приложений

Овладев основами Winsock, можно приступать к написанию приложений. Мы начнем с клиентского приложения, поскольку оно обычно проще серверного. Написание серверного приложения будет предметом следующего раздела.

Программа ClientApp.exe ожидает два аргумента в командной строке. Первый из них обязателен– это может быть IP-адрес или полностью определенное доменное имя хоста. Второй аргумент – номер порта; если же он не задан, то по умолчанию предполагается значение 80. Когда мы запускаем программу, указав адрес хоста, на котором работает Web-сервер, то получаем в ответ его начальную страницу (позже в этой главе мы расширим функциональность, позволив скачивать произвольные страницы с сервера). Программа может соединяться и с другими портами. Например, указав порт 25 (на нем работает почтовый протокол Simple Mail Transfer Protocol (SMTP)), мы полу- чим в ответ шапку, содержащую информацию о сервере. Некоторые службы, например, Telnet, работающая на порту 23, возвращают, на первый взгляд, «мусор», но это только кажется – просто Telnet таким образом предлагает ввести имя и пароль.

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

 

 

F

 

 

 

 

 

 

 

t

 

 

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

 

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

 

to

 

 

 

208 Глава 4. Сокеты на платформе Windows (Winsock)

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

 

 

.

 

 

 

 

g

.c

 

 

1

 

 

p

 

-xcha

 

 

 

 

 

 

 

 

 

 

Пример 4.3. Клиентское TCP*приложение

 

 

 

 

df

 

 

n

e

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1 #include <stdio.h>

 

 

 

 

 

 

 

 

 

 

 

 

2 #include <winsock2.h>

 

 

 

 

 

 

 

 

 

 

 

 

3

 

 

 

 

 

 

 

 

 

 

 

 

4 #pragma comment(lib,"ws2_32.lib")

 

 

 

 

 

 

 

 

 

 

 

5 #define STRING_MAX 1024

 

 

 

 

 

 

 

 

 

 

 

 

6 #define MAX

64000

 

 

 

 

 

 

 

 

 

 

 

7 char *client_send(char *targetip, int port);

 

 

 

 

 

 

 

 

 

 

8 {

 

 

9WSADATA wsaData;

10

WORD wVersionRequested;

11

struct hostent

pTarget;

12

struct sockaddr_in sock;

13

SOCKET

MySock;

14

wVersionRequested = MAKEWORD(2, 2);

15

 

 

16if (WSAStartup(wVersionRequested, &wsaData) < 0)

17{

18printf("################# ОШИБКА! ##############\n");

20printf("Ваша версия ws2_32.dll устарела.\n");

21printf("Скачайте и установите более свежую\n");

22printf("версию ws2_32.dll.\n");

23

24WSACleanup();

25exit(1);

26}

27MySock = socket(AF_INET, SOCK_STREAM, 0);

28if(MySock==INVALID_SOCKET)

29{

30printf("Ошибка сокета!\r\n");

31

32closesocket(MySock);

33WSACleanup();

34exit(1);

35}

36if ((pTarget = gethostbyname(targetip)) == NULL)

37{

38printf("Не удалось разрешить имя %s, попробуйте еще раз.\n",

targetip);

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

39closesocket(MySock);

40WSACleanup();

* Программа написана некорректно. Она никогда не прочтет страницу с Web-сервера, поскольку не посылает ему запроса (функция send() не вызывается). Кроме того, память, выделенная под массив output в строке 56, никогда не освобождается, так как предложение, в котором автор хотел ее освободить (строка 70), находится после возврата из функции. Впро- чем, кое-как программа работать будет: со службами SMTP и Telnet она сможет соединиться и

даже получить от них ответ (Прим. перев.)

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

 

 

-

 

 

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

 

to

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

 

 

 

e

 

 

 

 

d

 

 

xch41

 

 

 

 

 

 

 

f-

 

an

 

 

 

 

exit(1);

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

Программирование клиентских приложений 209

 

to

 

 

 

 

 

 

 

 

 

 

 

 

w Click

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

42}

43memcpy(&sock.sin_addr.s_addr, pTarget->h_addr, pTarget->h_length);

44sock.sin_family = AF_INET;

45sock.sin_port = htons( port );

46if ( (connect(MySock, (struct sockaddr *)&sock, sizeof (sock) )))

47{

48printf("Не могу соединиться с хостом.\n");

49closesocket(MySock);

50WSACleanup();

51exit(1);

52}

53char *recvString = new char[MAX];

54int nret;

55nret = recv(MySock, recvString, MAX + 1, 0);

56char *output= new char[nret];

57strcpy(output, "");

58if (nret == SOCKET_ERROR)

59{

60printf("Неудачная попытка получить данные. \n");

61}

62else

63{

64strncat(output, recvString, nret);

65delete [ ] recvString;

66}

67closesocket(MySock);

68WSACleanup();

69return (output);

70delete [ ] output;

71}

72int main(int argc, char *argv[])

73{

74int port = 80;

75char* targetip;

76

77if (argc < 2)

78{

79printf("ClientApp usage:\r\n");

80printf(" %s <TargetIP> [port]\r\n", argv[0]);

81return(0);

82}

83targetip = argv[1];

84if (argc >= 3)

85{

86port = atoi(argv[2]);

87}

88printf("%s", client_send(targetip, port) );

89return(0);

90}

 

 

 

 

hang

e

 

 

 

 

 

 

 

 

C

 

E

 

 

 

 

 

X

 

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

210 Глава 4. Сокеты на платформе Windows (Winsock)

w Click

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

m

w

 

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

 

 

 

 

 

e

 

 

 

 

df

 

 

n

 

Анализ

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

В строках 1–6 включаются заголовочный файлы, задается компоновка с библиотекой ws2_32.lib и определяются две константы. Константа STRING_MAX задает максимальную длину строки запроса. Значение 1024 достаточно для «нормальных» запросов. Но эксплойты и утилиты для проверки наличия уязвимостей, особенно в результате переполнения буфера, часто посылают гораздо более длинные запросы. Константа MAX определяет максимальную длину ответа, как правило HTMLстраницы. Она гораздо больше STRING_MAX, но и такого буфера может не хватить, тогда будет выдано сообщение об ошибке.

В строке 7 начинается определение функции client_send(), осуществляющей отправку и получение данных. Она принимает два параметра: IPадрес и номер порта, с которым мы хотим соединиться. Функция возвращает указатель на буфер, содержащий полученный от сервера ответ.

В строке 9 объявляется объект типа WSADATA, который взаимодействует с библиотекой ws2_32.dll.

В строке 10 объявлена переменная wVersionRequest òèïà WORD, которой мы позже воспользуемся для проверки номера версии ws2_32.dll.

В строке 11 объявлена переменная типа struct hostent, необходимая для разрешения имени хоста.

В строке 12 объявлена переменная типа struct sockaddr_in, в которой будет храниться адресная информация для сокета.

В строке 13 объявлена переменная типа SOCKET, в которую мы позже поместим дескриптор сокета.

В строках 16–26 инициализируется объект WSADATA и проверяется, что установлена подходящая версия ws2_32.dll. Если все хорошо, функция WSAStartup() вернет 0, в противном случае – отрицательное число. В программах посложнее иногда приходится выполнять более тщательный анализ кода ошибки, но в данном случае возврат отрицательного значения означает, скорее всего, что версия ws2_32.dll не годится.

В строке 27 создается сокет. Параметр AF_INET означает, что мы будем работать с протоколами сети Интернет. Следующий параметр может принимать одно из двух значений: SOCK_STREAM обычно означает протокол TCP, а SOCK_DGRAM – протокол UDP. Следовательно, чтобы создать TCP-соединение, нужно задать значение SOCK_STREAM. Далее мы проверяем, успешно ли был создан сокет.

В строке 36 переменная pTarget указывает на структуру, в которой хранятся результаты разрешения имени хоста. Если это сделать не удалось,

pTarget будет равна NULL, в таком случае мы завершаем программу.