利用“偵聽-轉發”程序破譯網管協議
發表于:2007-07-14來源:作者:點擊數:
標簽:
深圳市芯絡實業有限公司chenjun 一, 開發 目的及原理 本公司在產品開發過程中,需要研究多種以太網交換機(又稱智能集線器)的內部網管信息結構,為此,必須編寫出一個“竊聽”程序,把網管程序和交換機的通訊內容記錄下來加以分析。本人在Visual C++ 6.0下
深圳市芯絡實業有限公司 chenjun
一,
開發目的及原理
本公司在產品開發過程中,需要研究多種以太網交換機(又稱智能集線器)的內部網管信息結構,為此,必須編寫出一個“竊聽”程序,把網管程序和交換機的通訊內容記錄下來加以分析。本人在Visual C++ 6.0下用MFC Socket類編出程序,成功地實現了上述目的。
目前,標準的
網絡管理程序與支持網管的網絡設備之間大多采用標準的簡單網絡管理協議(SNMP)進行通訊。SNMP是一種高層協議,建立于UDP/IP之上。通訊雙方按照SNMP格式來傳遞各種網管信息和控制信息,并能進行事件實時報告或報警,從而使網絡管理員能方便及時地控制網絡當前的運行情況。
網管信息的范圍十分廣泛,如網絡流量,連接狀態等,因被管設備的不同而不同,廠家也能依照有關的國際標準自定義自家產品的網管信息。網管信息集中定義于管理信息庫(
MIB)中,整個體系是一個可擴展的樹狀結構。一條條的網管信息被包裝在SNMP協議包內,再往下傳給傳輸層,轉成UDP包,然后通過Socket 機制發送出去。
本程序的基本原理是:插到網管程序和被管設備之間“欺上瞞下”,與網管程序通訊時冒充被管設備;與被管設備通訊時冒充網管程序,使二者對本程序“無話不談”;本程序則暗中有序地記錄下談話內容,然后再“上傳下達”,將收到的內容轉發給真正的接收者,使談話繼續下去,如此循環不已。
二, 編程的思路和具體過程
本程序不需要復雜的圖形界面,因此,只需用Project Wizard開出一個支持Socket而基于對話框的MFC 應用程序即可。對話框的類名為
CChatDlg,然后再用資源編輯器在這個對話框上加上一個按鈕,面上文字為“Listen”。接收到的所有信息將在Visual C++集成環境的Output窗口中用TRACE語句打出,這樣做的目的是能方便及時地看到各種數據,當然也可用別的方法。在本程序中,Client指網管程序,Server指交換機。
接著給本項目添加兩個類,它們都衍生自CSocket,可調用ClassWizard工具生成。CClientSocket用于接收來自網管程序的UDP數據,而CServerSocket則用于接收來自交換機的UDP數據。這兩個類的定義如下:
class CClientSocket : public CSocket
{
// Attributes
public:
// Operations
public:
CClientSocket(CChatDlg* pdlg);
virtual ~CClientSocket();
// Overrides
public:
BOOL m_bFirst;
CChatDlg* pDlg;
// ClassWizard generated virtual
function overrides
//{{AFX_VIRTUAL(CClientSocket)
public:
virtual void OnReceive(int nErrorCode);
//}}AFX_VIRTUAL
// Generated message map functions
//{{AFX_MSG(CClientSocket)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
// Implementation
protected:
};
m_bFirst和pDlg是自定義的兩個類別成員, 其作用見下文。
class CServerSocket : public CSocket
{
// Attributes
public:
// Operations
public:
CServerSocket(CChatDlg* pdlg);
virtual ~CServerSocket();
// Overrides
public: CChatDlg* pDlg;
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CServerSocket)
public:
virtual void OnReceive(int nErrorCode);
//}}AFX_VIRTUAL
// Generated message map functions
//{{AFX_MSG(CServerSocket)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
// Implementation
protected:
};
然后,在CChatDlg類中加入對 按鈕Listen的處理函數如下:
void CChatDlg::OnListen()
{
pClientSocket = new CClientSocket(this);
if(pClientSocket != NULL)
{
if(!pClientSocket->Create(SNMP_SOCKET_PORT, SOCK_DGRAM))
AfxMessageBox("Can not create ClientSocket !");
else
::EnableWindow(GetDlgItem(IDC_LISTEN)-> m_hWnd,FALSE);
}
else
{
AfxMessageBox("Can not new ClientSocket !");
}
}
注意:SNMP_SOCKET_PORT應設為161。 然后,在CClientSocket中加入 虛函數OnReceive的實作內容:
void CClientSocket::OnReceive(int nErrorCode)
{
CSocket::OnReceive(nErrorCode);
unsigned char tmp[MAXTMPSIZE];
//MAXTMPSIZE是自定義宏,可為1024;
int i;
int RecNum;
UINT ClientPort;
CString ClientAddress;
if(m_bFirst)
{
m_bFirst = false;
RecNum = ReceiveFrom(tmp, MAXTMPSIZE, ClientAddress, ClientPort);
if(RecNum > 0)
{
TRACE("Received from client, %d bytes :\n", RecNum);
for(i=0; i<RecNum; i++)
{
if(i%10==0)
TRACE("\n%5d,", tmp[i]);
else
TRACE("%5d,", tmp[i]);
}
TRACE("\n\n");
pDlg->CreateServerSocket(ClientAddress, ClientPort);
pDlg->Send(true, tmp, RecNum);
}
else
AfxMessageBox("Error: fail to Receive from client the first time!");
}
else
{
RecNum = Receive(tmp, MAXTMPSIZE);
if(RecNum > 0)
{
TRACE("Received from client, %d bytes :\n", RecNum);
for(i=0; i<RecNum; i++)
{
if(i%10==0)
TRACE("\n%5d,", tmp[i]);
else
TRACE("%5d,", tmp[i]);
}
TRACE("\n\n");
pDlg->Send(true, tmp, RecNum);
}
else
AfxMessageBox("Error: fail to Receive from client!");
}
if(RecNum <= 0)
{
AfxMessageBox("Error: fail to Receive from client !");
return;
}
}
本段程序的大概意思是:如果本程序首次收到來自網管程序的UDP包,則要記錄下它的Socket端口號和IP 地址,這是本程序最關鍵的地方之一。這樣做的原因是:網管通訊開始時一般是由網管程序首先發出SNMP 請求包,所以要先響應網管程序;另一目的是由此獲得事先未知的網管程序偵聽的Socket端口號和IP地址,然后讓CChatDlg由此創建CServerSocket。隨后調用CChatDlg的Send函數將收到的UDP包轉發給交換機,并在Output窗口按每行10個的格式顯示出收到的數據。
上段程序中CChatDlg的Send和CreateServerSocket函數的內容如下:
void CChatDlg::CreateServerSocket (CString address, UINT port)
{
m_ClientAddress = address;
m_ClientPort = port;
pServerSocket = new CServerSocket(this);
if(pServerSocket != NULL)
{
if(!pServerSocket->Create(m_ClientPort, SOCK_DGRAM))
AfxMessageBox("Can not create ServerSocket !");
}
else
AfxMessageBox("Can not new ServerSocket !");
}
void CChatDlg::Send(BOOL ToServer, unsigned char* buf, int buf_len)
{
if(ToServer)
{
if(pServerSocket != NULL)
{
if(pServerSocket->SendTo(buf, buf_len, SNMP_SOCKET_PORT, m_ServerAddress)==SOCKET_ERROR)
AfxMessageBox("Error: fail to send data to server !");
}
}
else
{
if(pClientSocket != NULL)
{
if(pClientSocket->SendTo(buf, buf_len, m_ClientPort, m_ClientAddress)==SOCKET_ERROR)
AfxMessageBox("Error: fail to send data to client !");
}
}
}
注意:m_ServerAddress是交換機的IP地址,要事先在CChatDlg的OnInitDialog函數或其他地方設定。
最后,要處理接收到的來自于交換機的UDP包,將其中的數據在Output窗口中按每行10個的格式顯示出來,然后調用CChatDlg的Send函數將其轉發給網管程序。這在CServerSocket類的OnReceive虛函數中實現:
void CServerSocket::OnReceive(int nErrorCode)
{
CSocket::OnReceive(nErrorCode);
unsigned char tmp[MAXTMPSIZE];
int i;
int RecNum;
RecNum = Receive(tmp, MAXTMPSIZE);
if(RecNum > 0)
{
TRACE("Received from server, %d bytes:\n", RecNum);
for(i=0; i<RecNum; i++)
{
if(i%10==0)
TRACE("\n%5d,", tmp[i]);
else
TRACE("%5d,", tmp[i]);
}
TRACE("\n\n");
pDlg->Send(false, tmp, RecNum);
}
else
{
i = GetLastError();
TRACE("RecNum = %d, GetLastError() = %d\n", RecNum, i);
AfxMessageBox("Error: fail to Receive from server!");
}
}
以上就是本程序的主要功能部分,其中有一些變量因篇幅原因未作詳細解釋,但不影響對程序的理解。
三, 運行過程
分別在兩臺機器上裝上本程序和網管程序,將它們連上交換機,先運行本程序,點Listen按鈕,然后運行網管程序。一般的網管程序運行時,需要設置被管設備的IP的地址,此時,要將其設為本程序所在機器的IP地址,使網管程序將所有的SNMP包發給本程序。
之后兩程序應能正確運行(如果不行,可能要將上述過程多重復幾次。),在Output窗口可以看到數據源源不斷地顯示出來,這真是對網管過程的真實記錄!當數據量足夠后,結束本程序,可以看到網管程序界面上顯示出“設備已斷開連接!”的提示信息。然后可以將Output窗口中的數據拷貝到文本文件中,按照SNMP的格式和編碼規則進行詳細分析,網管協議就由此慢慢地破解出來了。
以上程序在Visual C++ 6.0下編譯通過并運行成功,實踐效果很好。
原文轉自:http://www.anti-gravitydesign.com