Программирование на C++ глазами хакера

         

Программирование на C++ глазами хакера

Изменение IP-адреса


Попытаюсь ответить на часто задаваемый вопрос и объяснить, как программно поменять IP-адрес. Зная, как это делается, легко можно написать программу, которая будет через определенные промежутки времени менять адрес компьютера. Это повысит безопасность компьютера, создав защиту от многих видов атак.

Сетевая карта может иметь несколько адресов одновременно, поэтому есть функции для добавления и удаления адресов. Для добавления воспользуйтесь функцией AddIPAddress, у которой следующие параметры:

IP-адрес;

маска сети для адреса;

индекс адаптера, для которого добавляется адрес;

контекст адреса. По своему опыту советую указывать нулевое значение. Контекст будет устанавливаться системой;

экземпляр, который чаще всего оставляют нулевым.

5.З. Окно будущей программы ChangeIPAddress

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

Зная контекст адреса, вы можете легко удалить адрес с помощью функции DeleteIPAddress, которой нужно передать в качестве единственного параметра именно этот контекст.

Продемонстрирую все сказанное на примере. Для этого создайте новое приложение MFC Application на основе диалога с именем ChangeIPAddress. На 5.3 показано главное окно будущей программы.

По нажатию кнопки List adapters информация о сетевых адресах будет получена и выведена в элементе List Box, который растянут вдоль нижней части окна. Код, который должен выполняться по нажатию этой кнопки, приведен в листинге 5.2.



Листинг 5.2. Вывод информации об установленных адресах
void CChangeIPAddressDlg::OnBnClickedButton3() { PIP_ADAPTER_INFO pAdapterInfo, pAdapter; ULONG iAdapterInfo; int iErr; CString Str;

iAdapterInfo = 0; iErr=GetAdaptersInfo(NULL, iAdapterInfo); if ((iErr!= 0) (iErr != ERROR_BUFFER_OVERFLOW)) { AfxMessageBox("GetAdaptersInfo failed"); return; }


if ((pAdapterInfo = (PIP_ADAPTER_INFO) GlobalAlloc(GPTR, iAdapterInfo)) == NULL) { AfxMessageBox("Memory allocation error\n"); return; }

if (GetAdaptersInfo(pAdapterInfo, iAdapterInfo) != 0) { AfxMessageBox("GetAdaptersInfo failed"); return; }

pAdapter = pAdapterInfo; lAdapters.AddString("======================"); while (pAdapter) { Str="Adapter: "; lAdapters.AddString(Str+pAdapter-AdapterName);

char s[20]; Str=itoa(pAdapter-Index, s, 10); Str="Index: "+Str; lAdapters.AddString(Str);

PIP_ADDR_STRING chAddr = (pAdapter-IpAddressList); while(chAddr) { lAdapters.AddString("------------------------------");

Str=itoa(chAddr-Context, s, 10); Str="Context: "+Str; lAdapters.AddString(Str);

Str="IP Address: "; lAdapters.AddString(Str+chAddr-IpAddress.String);

Str="Subnet Mask: "; lAdapters.AddString(Str+chAddr-IpMask.String);

chAddr = chAddr-Next; } pAdapter = pAdapter-Next; }

}

Вся информация об адресах получена так же, как и в разд. 5.1, с помощью функции GetAdaptersInfo. Напомню, что в качестве первого параметра нужно указать структуру типа PIP_ADAPTER_INFO. В этой структуре в параметре Index хранится индекс сетевого устройства, который надо будет указывать в качестве третьего параметра функции AddIPAddress при добавлении нового IP, а в параметре IpAddressList — массив из структур типа PIP_ADDR_STRING. В этой структуре нас интересует параметр Context, в котором хранится контекст IP-адреса. В параметре IpAddress хранится адрес, а в IpMask находится маска сети.

По нажатию кнопки Change IP добавляется новый адрес для сетевого адаптера. Можно перед добавлением найти и удалить все уже существующие адреса, а потом присоединить новый. Пример кода, который должен выполнятся по нажатию этой кнопки, приведен в листинге 5.3.

Листинг 5.3. Добавление нового адреса для первого сетевого адаптера в системе
void CChangeIPAddressDlg::OnBnClickedButton1() { PIP_ADAPTER_INFO pAdapterInfo, pAdapter; ULONG iAdapterInfo; int iErr; ULONG iInst, iContext; iInst=iContext=0;



iAdapterInfo = 0; iErr=GetAdaptersInfo(NULL, iAdapterInfo); if ((iErr!= 0) (iErr != ERROR_BUFFER_OVERFLOW)) { AfxMessageBox("GetAdaptersInfo failed"); return; }

if ((pAdapterInfo = (PIP_ADAPTER_INFO) GlobalAlloc(GPTR, iAdapterInfo)) == NULL) { AfxMessageBox("Memory allocation error\n"); return; }

if (GetAdaptersInfo(pAdapterInfo, iAdapterInfo) != 0) { AfxMessageBox("GetAdaptersInfo failed"); return; }

pAdapter = pAdapterInfo;

char sNewAddr[20], sNewMask[20];

eIPEdit.GetWindowText(sNewAddr, 20); eMaskEdit.GetWindowText(sNewMask, 20);

iErr=AddIPAddress(inet_addr(sNewAddr), inet_addr(sNewMask), pAdapter-Index, iContext, iInst); if (iErr!=0) AfxMessageBox("Can't change address"); }

Чтобы добавить новый адрес, необходимо знать индекс сетевого адаптера. Для его определения используется функция GetAdaptersInfo. После этого можно вызывать функцию AddIPAddress.

По нажатию кнопки Del IP будет удаляться адрес с контекстом, указанным в поле IP Context. Код, который должен выполняться по нажатию этой кнопки, можно увидеть в листинге 5.4.

Листинг 5.4. Удаление IP-адреса
void CChangeIPAddressDlg::OnBnClickedButton2() { char sContext[20]; eContext.GetWindowText(sContext, 20);

int Context=atoi(sContext); if (DeleteIPAddress(Context) != 0) { AfxMessageBox("Can't delete address"); } }

Интересного эффекта можно добиться, если просто удалить все IP-адреса. В этом случае компьютер исчезнет из сети и не сможет с ней работать. Но это уже из серии программ-шуток.

Напоследок хотел бы вас предупредить, что функции будут корректно работать только при правильно настроенной сети. Даже если просто выдернут сетевой кабель, функции не работают. На 5.4 показан результат работы программы на моем ноутбуке. Перед нажатием кнопки List adapters я отключил сетевой кабель, и в результате IP-адрес и маска сети стали нулевыми (0.0.0.0).



5.4. Результат работы программы ChangeIPAddress без сетевого кабеля

Примечание
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter5\ChangeIPAddress.

Параметры сети


В Windows 9x была очень удобная и полезная утилита WinIPConfig, которая отображала параметры сети. С помощью этой утилиты легко можно было узнать IP-адрес каждого сетевого устройства или МАС-адрес.

Сетевой МАС-адрес является уникальным и прошит в памяти сетевого устройства. Это свойство МАС-адреса стали использовать для обеспечения безопасности или защиты программ. Если в компьютере есть сетевая карта, то ее уникальный номер получить достаточно просто.

Для работы с параметрами сети используется библиотека IPHlpApi.lib. Давайте рассмотрим пример, и на его основе я познакомлю вас с самыми интересными функциями.

Создайте новое приложение MFC-Application на базе диалогового окна. Расположите в главном окне пять полей Edit Control, один List Box и кнопку с надписью Get info. Окно, которое получилось у меня, вы можете увидеть на 5.1.

5.1. Окно будущей программы VisualIPConfig

Для полей ввода создайте следующие переменные: eHostName, DNSServers, eNodeType, eIPRouting, eWinsProxy. Для списка введите переменную eAdaptersInfo.

Сетевых устройств может быть несколько, поэтому информация о них будет выводиться в список, а общая информация будет отображаться в полях ввода.

Создайте обработчик события BN_CLICKED для кнопки (для этого можно просто дважды щелкнуть по ней) и в него добавьте содержимое листинга 5.1. Я советую набрать код вручную, а не использовать пример с диска.

Листинг 5.1. Определение параметров сетевой карты
void CVisualIPConfigDlg::OnBnClickedButton1() { PFIXED_INFO pFixedInfo; ULONG iFixedInfo;

PIP_ADAPTER_INFO pAdapterInfo, pAdapter; ULONG iAdapterInfo; PIP_ADDR_STRING chAddr;

CString Str; TCHAR lpszText[1024]; int iErr;

if ((iErr = GetNetworkParams(NULL, iFixedInfo)) != 0) { if (iErr != ERROR_BUFFER_OVERFLOW) { AfxMessageBox("GetNetworkParams failed"); return; } }

if ((pFixedInfo=(PFIXED_INFO)GlobalAlloc(GPTR, iFixedInfo))==NULL) { AfxMessageBox("Memory allocation error"); return; }

if (GetNetworkParams(pFixedInfo, iFixedInfo) != 0) { AfxMessageBox("GetNetworkParams failed"); return; }


eHostName.SetWindowText(pFixedInfo-HostName);

CString s=pFixedInfo-DnsServerList.IpAddress.String; chAddr = pFixedInfo-DnsServerList.Next; while(chAddr) { s=s+" "+chAddr-IpAddress.String; chAddr = chAddr-Next; } DNSServers.SetWindowText(s);

switch (pFixedInfo-NodeType) { case 1: eNodeType.SetWindowText("Broadcast"); break; case 2: eNodeType.SetWindowText("Peer to peer"); break; case 4: eNodeType.SetWindowText("Mixed"); break; case 8: eNodeType.SetWindowText("Hybrid"); break; default: eNodeType.SetWindowText("Don't know"); }

eIPRouting.SetWindowText(pFixedInfo-EnableRouting ? "Enabled" : "Disabled"); eWinsProxy.SetWindowText(pFixedInfo-EnableProxy ? "Enabled" : "Disabled");

iAdapterInfo = 0; iErr=GetAdaptersInfo(NULL, iAdapterInfo); if ((iErr!= 0) (iErr != ERROR_BUFFER_OVERFLOW)) { AfxMessageBox("GetAdaptersInfo failed"); return; }

if ((pAdapterInfo = (PIP_ADAPTER_INFO) GlobalAlloc(GPTR, iAdapterInfo)) == NULL) { AfxMessageBox("Memory allocation error\n"); return; }

if (GetAdaptersInfo(pAdapterInfo, iAdapterInfo) != 0) { AfxMessageBox("GetAdaptersInfo failed"); return; }

pAdapter = pAdapterInfo;

eAdaptersInfo.AddString("===========================");

while (pAdapter) { switch (pAdapter-Type) { case MIB_IF_TYPE_ETHERNET: Str="Ethernet adapter: "; break; case MIB_IF_TYPE_PPP: Str="PPP adapter: "; break; case MIB_IF_TYPE_LOOPBACK: Str="Loopback adapter: "; break; case MIB_IF_TYPE_TOKENRING: Str=" Token Ring adapter: "; break; case MIB_IF_TYPE_FDDI: Str="FDDI adapter: "; break; case MIB_IF_TYPE_SLIP: Str="Slip adapter: "; break; case MIB_IF_TYPE_OTHER: default: Str="Other adapter: "; } eAdaptersInfo.AddString(Str+pAdapter-AdapterName);

Str= "Description: "; eAdaptersInfo.AddString(Str+pAdapter-Description);



Str="Physical Address: "; for (UINT i=0; ipAdapter-AddressLength; i++) { if (i == (pAdapter-AddressLength - 1)) sprintf(lpszText, "%.2X",(int)pAdapter-Address[i]); else sprintf(lpszText, "%.2X",(int)pAdapter-Address[i]); Str=Str+lpszText; } eAdaptersInfo.AddString(Str);

sprintf(lpszText, "DHCP Enabled: %s", (pAdapter-DhcpEnabled ? "yes" : "no")); eAdaptersInfo.AddString(lpszText);

chAddr = (pAdapter-IpAddressList); while(chAddr) { Str="IP Address: "; eAdaptersInfo.AddString(Str+chAddr-IpAddress.String);

Str="Subnet Mask: "; eAdaptersInfo.AddString(Str+chAddr-IpMask.String);

chAddr = chAddr-Next; }

Str="Default Gateway: "; eAdaptersInfo.AddString(Str+pAdapter-GatewayList.IpAddress.String);

chAddr = pAdapter-GatewayList.Next; while(chAddr) { //print next Gateway chAddr = chAddr-Next; }

Str="DHCP Server: "; eAdaptersInfo.AddString(Str+pAdapter-DhcpServer.IpAddress.String);

Str="Primary WINS Server: "; eAdaptersInfo.AddString(Str+pAdapter-PrimaryWinsServer.IpAddress.String);

Str="Secondary WINS Server: "; eAdaptersInfo.AddString(Str+pAdapter-SecondaryWinsServer.IpAddress.String);

eAdaptersInfo.AddString("==========================="); pAdapter = pAdapter-Next; } }

Общую информацию о сети можно получить с помощью функции GetNetworkParams. У нее два параметра: структура типа PFIXED_INFO и размер структуры.

Если первый параметр оставить нулевым, а в качестве второго указать числовую переменную, то в эту переменную запишется размер памяти, необходимый для структуры PFIXED_INFO. Именно это и делается первый раз. Память надо выделять в глобальной области с помощью функции GlobalAlloc, иначе функция может вернуть некорректные данные.

После этого функция GetNetworkParams вызывается еще раз, но с указанием двух параметров. Если результатом выполнения функции будет 0, то получение данных прошло успешно.



Теперь разберем параметры структуры PFIXED_INFO:

HostName — имя компьютера;

DnsServerList.IpAddress — список IP-адресов серверов DNS;

NodeType — тип сетевого устройства;

EnableRouting — если равно TRUE, то маршрутизация включена;

EnаblеРrоху — если равно TRUE, то кэширование включено.

Получив общую информацию, можно приступить к перечислению параметров всех установленных адаптеров. Для этого используется функция GetAdaptersInfо. У нее также два параметра: переменная типа PIP_ADAPTER_INFO и размер. Если первый параметр указать нулевым, то через второй параметр функция вернет необходимый размер для структуры PIP_ADAPTER_INFO.

Рассмотрим параметры полученной структуры PIP_ADAPTER_INFO:

Tуре — тип адаптера. Может принимать одно из следующих значений:

MIB_IF_TYPE_ETHERNET — сетевой адаптер Ethernet;

MIB_IF_TYPE_TOKENRING — адаптер Token Ring;

MIB_IF_TYPE_FDDI — адаптер FDDI;

MIB_IF_TYPE_PPP — РРР-адаптер;

MIB_IF_TYPE_LOOPBACK — адаптер LoopBack;

MIB_IF_TYPE_SLIP — Slip-адаптер ;

MIB_IF_TYPE_OTHER — другое;

AdapterName — имя адаптера;

Description — описание, которое может хранить название фирмы-производителя или предназначение;

AddressLength — длина МАС-адреса;

Address — МАС-адрес;

DhcpEnabled — принимает значение TRUE, если включен DHCP;

IpAddressList — список IP -адресов и масок сети. Каждый адаптер может иметь одновременно несколько адресов;

GatewayList — список шлюзов;

DhcpServer — адреса DHCP -серверов;

PrimaryWinsServer — адрес первичного WINS-сервера;

SecondaryWinsServer — адрес вторичного WINS-сервера.

Для компиляции примера необходимо открыть свойства проекта и в разделе Lnker/Input добавить библиотеку IPHlpApi.lib в свойство Additional Dependencies. А в начале модуля нужно добавить описание заголовочного файла iphlpapi.h.

Запустите файл VisualIPConfig.exe. Нажмите кнопку Get info. Все сведения о сетевой карте, полученные с помощью программы, представлены на 5.2.



5.2. Результат работы программы VisualIPConfig

Примечание
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter5\VisualIPConfig.

Подвисшие файлы


Вы уже немного познакомились с файлами, а в разд. 4.2 даже попробовали создать файл на удаленной машине и записать в него данные. Теперь посмотрим, как можно превратить работу с файлами в небольшую шалость.

Вспомните, как формируется сетевой путь:

\\Имя компьютера\диск\путь

Если нужно обратиться к локальному диску как сетевому, и при этом диск не является открытым, то после имени диска необходимо поставить знак $. Например, чтобы получить доступ к файлу myfile.txt на диске С:, нужно написать следующий путь:

\\MyComputer\C$\myfile.txt

Теперь самое интересное. В Windows нельзя создавать файлы с именами, содержащими знак ?, и проверка на такой знак делается на уровне ОС. Но если обращаться к файлу по сети, то проверка не выполняется. Если у вас есть сеть, то подключите любой сетевой диск другого компьютера. Допустим, что этому диску будет назначена буква е:. Теперь, если выполнить код из листинга 5.7, то программа зависнет, и снять ее будет невозможно.

Листинг 5.7. Создание неправильного файла по сети
if ((FileHandle = CreateFile("\\\\notebook\\e$\\?myfile.txt", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) { MessageBox(0, "Create file error", "Error",0); return; }

// Write to file 9 symbols if (WriteFile(FileHandle, "Test line", 9, BWritten, NULL)== 0) { MessageBox(0, "Write to file error", "Error",0); return; }

// Close file CloseHandle(FileHandle);

В листинге 5.7 я пытался создать файл и записать в него 9 символов, как и в разд. 4.2. Но имя файла написано неверно (присутствует символ ?), поэтому создание невозможно, а проверка на недопустимый символ отсутствует. Именно поэтому программа зависает в ожидании ответа от ОС, которого не будет.

Примечание
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter5\TestFile.



Работа с СОМ-портом


Мне по долгу службы часто приходилось работать с интерфейсом RS-232. Так в официальной документации называется СОМ-порт компьютера. Современное оборудование (контроллеры, устройства сбора информации и т.д.) работают через этот порт. К любому модему, даже внутреннему, обращение происходит именно через СОМ-порт. А сколько существует внешних устройств, подключаемых по этому интерфейсу, сосчитать невозможно.

Работа с портами похожа на работу с файлами. Давайте рассмотрим простейший пример. Для этого создайте новое приложение MFC Application на основе диалога с именем COMport. Внешний вид главного окна будущей программы вы можете увидеть на 5.5.

5.5. Окно будущей программы Comport

В верхней части окна находится выпадающий список Combo Box, в котором можно выбирать имя порта. Рядом со списком две кнопки: для открытия и закрытия порта. Чуть ниже расположены текстовое поле для ввода команды и кнопка для ее отправки.

В центре окна расположились элементы управления List Box: для отображения хода работы с портом и многострочное поле ввода для отображения пришедших данных.

Создайте подобный интерфейс, и можно переходить к программированию. По нажатию кнопки Open port должен выполняться код из листинга 5.5.

Листинг 5.5. Открытие порта
void CCOMportDlg::OnBnClickedOpenportButton() { if (hCom != INVALID_HANDLE_VALUE) { OnBnClickedButton1(); Sleep(300); }

char sPortName[10]; cbPorts.GetWindowText(sPortName, 10);

hCom = CreateFile(sPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);

if (hCom == INVALID_HANDLE_VALUE) lLogList.AddString("Error opening port"); else { lLogList.AddString("Port successfully opened."); hThread = CreateThread(0, 0, ReadThread, (LPVOID)this, 0, 0);

DCB dcb; GetCommState(hCom, dcb); dcb.BaudRate = CBR_57600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; if (SetCommState(hCom, dcb)) lLogList.AddString("Configuring OK"); else lLogList.AddString("Configuring Error"); } }


Если попытаться открыть порт дважды, то будет получено сообщение об ошибке, поэтому первым делом нужно произвести эту проверку. И если порт открыт, то закрыть его. Эта проверка выполняется в функции onBnClickedButton1, которую я покажу чуть позже, и она будет вызываться при нажатии на кнопку Close port.

Теперь получим имя выбранного порта и откроем его. Для этого используется функция работы с простыми файлами CreateFile, только вместо имени файла указывается имя порта.

Если порт открыт удачно, то выводится соответствующее сообщение, и можно перейти к конфигурированию параметров соединения. Для этого сначала получите текущие настройки системы с помощью функции GetCommState. Ей нужно передать два параметра: указатель на открытый порт и указатель на структуру типа DCB. Эта структура содержит полную информацию о параметрах соединения и выглядит следующим образом:

typedef struct _DCB { DWORD DCBlength; // размер структуры DCB DWORD BaudRate; // скорость передачи данных в бодах DWORD fBinary: 1; // двоичный режим без проверки конца // строки DWORD fParity: 1; // включить проверку четность DWORD fOutxCtsFlow:1; // CTS-правление потоком выхода DWORD fOutxDsrFlow:1; // DSR-управление потоком выхода DWORD fDtrControl:2; // DTR-тип управления потоком скорости // передачи данных DWORD fDsrSensitivity:1; // DSR-чувствительность DWORD fTXContinueOnXoff:1; // стоп-сигнал продолжает выполнение DWORD fOutX: 1; // старт/стоп-сигнал для управления // выходящим потоком DWORD fInX: 1; // старт/стоп - сигнал для управления // входящим потоком DWORD fErrorChar: 1; // включить проверку погрешностей DWORD fNull: 1; // отвергать пустой поток данных DWORD fRtsControl:2; // RTS - управление потоком данных DWORD fAbortOnError:1; // проверять операции чтения/записи DWORD fDummy2:17; // зарезервировано WORD wReserved; // зарезервировано WORD XonLim; // порог чувствительности старт-сигнала WORD XoffLim; // порог чувствительности стоп-сигнала BYTE ByteSize; // количество бит (обычно 7 или 8) BYTE Parity; // четность байта BYTE StopBits; // стоповые биты char XonChar; // вид старт-сигнала в потоке char XoffChar; // вид стоп-сигнала в потоке char ErrorChar; // вид сигнала погрешности



char EofChar; // сигнал окончания потока char EvtChar; // зарезервировано WORD wReserved1; // зарезервировано } DCB;

Если неправильно указаны параметры, то данные не будут передаваться и приниматься. Самое главное — заполнить следующие поля:

BaudRate — скорость передачи данных (бит/с). Указывается константа в виде CBR_скорость, где скорость должна быть равна скорости, поддерживаемой используемым устройством, например, 56000;

ByteSize — размер передаваемого байта (может быть 7 или 8);

Parity — флаг проверки четности;

StopBits — стоповые биты, могут принимать значения ONESTOPBIT (один), ONE5STOPBITS (полтора) или TWOSTOPBITS (два).

Остальные параметры можно оставить по умолчанию (те, которые вернула система). Но прежде чем указывать какие-либо параметры, обязательно прочитайте документацию на аппаратуру, с которой необходимо соединяться. Я не встречал устройств, которые поддерживали бы все режимы работы. Например, модем ZyXel Omni 56 K может поддерживать скорость от 2400 до 56 000, и можно указывать значения только из этого диапазона.

Помимо этого, нужно, чтобы оба устройства (передающее и принимающее) были настроены одинаково (скорость, размер байта и т. д.), иначе данные не будут передаваться.

После конфигурирования порта запускается поток, в котором мы будем бесконечно пытаться считать данные из порта. Это, конечно же, не эффективно, потому что удобнее использовать сообщения Windows, но для простого примера в обучающих целях достаточно. Функция чтения потока ReadThread выглядит следующим образом:

DWORD __stdcall ReadThread(LPVOID hwnd) { DWORD iSize; char sReceivedChar; while(true) { ReadFile(hCom,sReceivedChar,1,iSize,0); SendDlgItemMessage((HWND)hwnd,IDC_EDIT2,WM_CHAR, sReceivedChar,0); } }

В этой функции вы можете увидеть бесконечный цикл чтения данных, которое выполняется стандартной функцией чтения из файла — ReadFile.

Теперь посмотрите на функцию закрытия порта, которая будет вызываться по нажатию кнопки Close port:

void CCOMportDlg::OnBnClickedButton1() { if (hCom == INVALID_HANDLE_VALUE) return;



if (MessageBox("Close port?", "Warning", MB_YESNO) == IDYES) { TerminateThread(hThread, 0); CloseHandle(hCom); hCom = INVALID_HANDLE_VALUE; } }

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

Если порт открыт, то выводится запрос на подтверждение закрытия порта. Если пользователь подтвердит, то прерывается поток, закрывается указатель порта и переменной hCom присваивается значение INVALID_HANDLE_VALUE.

И последнее, что предстоит добавить в программу, — возможность отправки сообщений. Для этого по нажатию кнопки Send command должен выполняться код из листинга 5.6.

Листинг 5.6. Функция отправки данных в порт
void CCOMportDlg::OnBnClickedSendcommandButton() { if (hCom == INVALID_HANDLE_VALUE) { AfxMessageBox("Open port before send command"); return; }

char sSend[10224]; eSendCommand.GetWindowText(sSend, 1024);

if (strlen(sSend)0) { lLogList.AddString(sSend);

sSend[strlen(sSend)] = '\r'; sSend[strlen(sSend)] = '\0';

TerminateThread(hThread,0); DWORD iSize; WriteFile(hCom, sSend, strlen(sSend), iSize,0); hThread = CreateThread(0, 0, ReadThread, (LPVOID)this, 0, 0); } }

Сначала проверяется, открыт ли порт. Если он уже закрыт или никогда не открывался, то нет смысла писать в него данные. После этого надо получить данные для отправки, и если они больше нуля, то добавить в конец отправляемой строки символ завершения строки (нулевой символ). Мне приходилось работать с разным оборудованием, и большинство типов требует в конце команды отправлять символы конца строки и перевода каретки. Иногда бывает достаточно только символа конца строки.

Теперь прерываем поток чтения данных и записываем в порт данные стандартной функцией работы с файлами WriteFile. После записи можно снова запускать поток чтения.

Если у вас есть модем, то можете запустить программу и открыть порт, на котором настроен модем. Отправьте команду ATDTxxxxx, где хххх — это номер телефона. Модем должен будет начать набор указанного номера телефона.

Примечание
Исходный код примера, описанного в этом разделе, вы можете найти на компакт - диске в каталоге \Demo\Chapter5\COMport.

Работа с железом


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

Так как в данной книге упор сделан на работу с сетью, то и здесь я коснусь этой темы. При написании сетевых программ очень часто надо знать параметры локального компьютера, а иногда даже уметь изменять их. Я покажу, как определить параметры сетевой карты и сетевые настройки. Прочитав эту главу, вы найдете ответ на очень часто возникающий у начинающих программистов вопрос: "Как узнать IP-адрес локального компьютера?"

Помимо этого, достаточно подробно будет описана работа с СОМ-портами, которые часто используются при подключении к компьютеру различного оборудования. Когда я занимался автоматизацией производства, мне пришлось написать множество программ, которые через СОМ-порты собирают данные с устройств или наблюдают за работой аппаратуры.