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


Структура сети


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

Создайте новое MFC-приложение в Visual C++ и назовите проект NetNeighbour. В Мастере создания приложений, в разделе Application Type выберите Dialog based, а в разделе Advanced Features — Windows sockets. На жмите кнопку Finish, чтобы среда разработки создала необходимый шаблон приложения.

Прежде чем приступать к программированию, необходимо оформить окно будущей программы. Откройте в редакторе ресурсов диалоговое окно IDD_NETNEIGHBOUR_DIALOG. Растяните по всей свободной поверхности компонент Tree Control ( 4.2).

Чтобы можно было работать с этим компонентом, щелкните по нему правой кнопкой мышки. В появившемся меню выберите пункт Add variable, а в поле Variable name укажите m_NetTree. Эта переменная понадобится для добавления в меню новых пунктов.

4.2. Использование компонента Tree Control

Теперь все готово для рассмотрения исходного кода. Перейдите в файл NetNeighbourDlg.cpp. Здесь найдите функцию OnInitDialog, которая вызывается во время инициализации окна. В этот момент необходимо создать корневой элемент дерева. Это должно происходить следующим образом:

m_hNetworkRoot = InsertTreeItem(TVI_ROOT, NULL, "My Net", DRIVE_RAMDISK +1);

В переменной m_hNetworkRoot сохраняется результат работы функции InsertTreeItem.

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



Листинг 4.2. Добавление нового элемента в дерево сети
HTREEITEM CNetNeighbourDlg::InsertTreeItem(HTREEITEM hParent, NETRESOURCE *const pNetResource, CString sText, int iImage) { TVINSERTSTRUCT InsertStruct; InsertStruct.hParent = hParent; InsertStruct.hInsertAfter = TVI_LAST; InsertStruct.itemex.mask = TVIF_IMAGE | TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM; InsertStruct.itemex.pszText = sText.GetBuffer(sText.GetLength()); InsertStruct.itemex.iImage = iImage; InsertStruct.itemex.cChildren = 1; InsertStruct.itemex.lParam = (LPARAM)pNetResource; sText.ReleaseBuffer(); return m_NetTree.InsertItem( InsertStruct ); }


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

Для этого надо написать обработчик события ITEMEXPANDING и в нем производить поиск. Перейдите в редактор ресурсов и выделите компонент Tree Control . В окне Properties щелкните по кнопке Control Events, и вы увидите все события, которые может генерировать выделенный компонент. Щелкните напротив события TVN_ITEMEXPANDING и в выпадающем списке выберите пункт Add, чтобы добавить обработчик события. Код, который должен быть в этом обработчике, приведен в листинге 4.3.

Листинг 4.3. Обработчик события TVN_ITEMEXPANDING
void CNetNeighbourDlg::OnTvnItemexpandingTree1(NMHDR *pNMHDR, LRESULT *pResult) { LPNMTREEVIEW pNMTreeView = reinterpret_castLPNMTREEVIEW(pNMHDR); // TODO: Add your control notification handler code here // (Добавьте сюда ваш код обработки сообщения)

CWaitCursor CursorWaiting; ASSERT(pNMTreeView); ASSERT(pResult);

if (pNMTreeView-action == 2) { CString sPath = GetItemPath(pNMTreeView-itemNew.hItem);

if(!m_NetTree.GetChildItem(pNMTreeView-itemNew.hItem)) { EnumNetwork(pNMTreeView-itemNew.hItem); if( m_NetTree.GetSelectedItem( ) != pNMTreeView- itemNew.hItem) m_NetTree.SelectItem(pNMTreeView-itemNew.hItem); } }

*pResult = 0; }

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

Листинг 4.4. Функция EnumNetwork для просмотра сети
bool CNetNeighbourDlg::EnumNetwork(HTREEITEM hParent) { bool bGotChildren = false;

NETRESOURCE *const pNetResource = (NETRESOURCE *) (m_NetTree.GetItemData(hParent));

DWORD dwResult; HANDLE hEnum; DWORD cbBuffer = 16384; DWORD cEntries = 0xFFFFFFFF; LPNETRESOURCE lpnrDrv; DWORD i; dwResult = WNetOpenEnum(pNetResource ? RESOURCE_GLOBALNET : RESOURCE_CONTEXT, RESOURCETYPE_ANY, 0, pNetResource ? pNetResource: NULL, hEnum );



if (dwResult != NO_ERROR) { return false; }

do { lpnrDrv = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer); dwResult = WNetEnumResource(hEnum, cEntries, lpnrDrv, cbBuffer); if (dwResult == NO_ERROR) { for(i = 0; icEntries; i++) { CString sNameRemote = lpnrDrv[i].lpRemoteName; int nType = 9; if(sNameRemote.IsEmpty()) { sNameRemote = lpnrDrv[i].lpComment; nType = 8; } if (sNameRemote.GetLength() 0 sNameRemote[0] == _T('\\')) sNameRemote = sNameRemote.Mid(1); if (sNameRemote.GetLength() 0 sNameRemote[0] == _T('\\')) sNameRemote = sNameRemote.Mid(1);

if (lpnrDrv[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE) { int nPos = sNameRemote.Find( _T('\\')); if(nPos = 0) sNameRemote = sNameRemote.Mid(nPos+1); InsertTreeItem(hParent, NULL, sNameRemote, DRIVE_NO_ROOT_DIR); } else { NETRESOURCE* pResource = new NETRESOURCE; ASSERT(pResource); *pResource = lpnrDrv[i]; pResource-lpLocalName = MakeDynamic(pResource-lpLocalName); pResource-lpRemoteName = MakeDynamic(pResource-lpRemoteName); pResource-lpComment = MakeDynamic(pResource-lpComment); pResource-lpProvider = MakeDynamic(pResource-lpProvider); InsertTreeItem(hParent, pResource, sNameRemote, pResource-dwDisplayType+7); } bGotChildren = true; } } GlobalFree((HGLOBAL)lpnrDrv); if (dwResult != ERROR_NO_MORE_ITEMS) break; } while (dwResult != ERROR_NO_MORE_ITEMS);

WNetCloseEnum(hEnum); return bGotChildren; }

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

DWORD WNetOpenEnum( DWORD dwScope, // scope of enumeration DWORD dwType, // resource types to list DWORD dwUsage, // resource usage to list LPNETRESOURCE lpNetResource, // pointer to resource structure LPHANDLE lphEnum // pointer to enumeration handle buffer );

Функция открывает перечисление сетевых устройств в локальной сети. Рассмотрим передаваемые ей параметры:

dwScope — ресурсы, включаемые в перечисление. Возможны комбинации следующих значений:




    RESOURCE_GLOBALNET — все ресурсы сети;

    RESOURCE_CONNECTED — подключенные ресурсы;

    RESOURCE_REMEMBERED — запомненные ресурсы;

    dwType — тип ресурсов, включаемых в перечисление. Возможны комбинации следующих значений:


      RESOURCETYPE_ANY — все ресурсы сети;

      RESOURCETYPE_DISK — сетевые диски;

      RESOURCETYPE_PRINT — сетевые принтеры;

      dwUsage — использование ресурсов, включаемых в перечисления. Возможны следующие значения:


        0 — все ресурсы сети;

        RESOURCEUSAGE_CONNECTABLE — подключаемые;

        RESOURCEUSAGE_CONTAINER — контейнерные;

        lpNetResource — указатель на структуру NETRESOURCE. Если этот параметр равен нулю, то перечисление начинается с самой верхней ступени иерархии сетевых ресурсов. Ноль ставится для того, чтобы получить самый первый ресурс. Потом я передаю в качестве этого параметра указатель на уже найденный ресурс. Тогда перечисление начнется с него и продолжится дальше. Так я повторяю, пока не найдутся все ресурсы;

        lphEnum — указатель, который понадобится В функции WnetEnumResource.

        Теперь нужно рассмотреть структуру NETRESOURCE:

        typedef struct _NETRESOURCE { DWORD dwScope; DWORD dwType; DWORD dwDisplayType; DWORD dwUsage; LPTSTR lpLocalName; LPTSTR lpRemoteName; LPTSTR lpComment; LPTSTR lpProvider; } NETRESOURCE;

        Что такое dwScope, dwType и dwUsage, вы уже знаете. А вот остальные рассмотрим подробнее:

        dwDisplayType — способ отображения ресурса:


        RESOURCEDISPLAYTYPE_DOMAIN — это домен;

        RESOURCEDISPLAYTYPE_GENERIC — нет значения;

        RESOURCEDISPLAYTYPE_SERVER — сервер;

        RESOURCEDISPLAYTYPE_SHARE — разделяемый ресурс;

        lpLocalName — локальное имя;

        lpRemoteName — удаленное имя;

        lpComment — комментарий;

        lpProvider — хозяин ресурса. Параметр может быть равен нулю, если хозяин неизвестен.

        Теперь можно переходить к следующей функции:

        DWORD WNetEnumResource( HANDLE hEnum, // handle to enumeration LPDWORD lpcCount, // pointer to entries to list LPVOID lpBuffer, // pointer to buffer for results LPDWORD lpBufferSize // pointer to buffer size variable );



        Параметры функции WnetEnumResource:

        hEnum — указатель на возвращенное функцией wNetopenEnum значение;

        lpcCount — максимальное количество возвращаемых значений. Не стесняйтесь, ставьте 2000. Если вы зададите 0xFFFFFFFF, то перечислятся все ресурсы. После выполнения функция передаст сюда фактическое число найденных ресурсов;

        lpBuffer — указатель на буфер, в который будет помещен результат;

        lpBuffersize — размер буфера.

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

        Это все, что касается поиска открытых сетевых ресурсов. Осталось только сделать одно замечание. Функция поиска WNetOpenEnum и соответствующие ей структуры находятся в библиотеке mpr.lib, которая по умолчанию не линкуется к проекту. Чтобы собрать проект без ошибок, необходимо подключить эту библиотеку. Для этого щелкните правой кнопкой мышки по имени проекта в окне Solution Explorer и в появившемся меню выберите пункт Properties. Перед вами откроется окно свойств, в котором надо перейти в раздел Configuration Properties/Linker/Input. Здесь в строке Additional Dependencies напишите имя библиотеки mpr.lib ( 4.3).



        4.3. Добавление библиотеки, содержащей функцию WNetOpenEnum

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

        Содержание раздела