成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

網絡安全編程:Winsock編程基礎

安全
網絡攻防是一個比較大的話題,比如端口掃描、SQL Injection掃描、數據包嗅探、網絡密碼猜解、后門、木馬等知識的基礎技術。這些技術在入侵剖析中是比較常見的技術。

 [[378479]]

網絡攻防是一個比較大的話題,比如端口掃描、SQL Injection掃描、數據包嗅探、網絡密碼猜解、后門、木馬等知識的基礎技術。這些技術在入侵剖析中是比較常見的技術。

在學習掃描器、嗅探器、木馬等知識之前,首先必須學習網絡編程的基礎知識,網絡編程的基礎是深入學習網絡的起步,沒有基礎知識,掃描、嗅探都是空談。

01 網絡基礎知識

各計算機之間通過互聯網進行通信主要依賴TCP/IP。該協議是一個4層協議,由上至下分別是應用層、傳輸層、網際層和鏈路層。TCP/IP的下層協議總是為上層協議服務,下層協議的細節對于上層協議來說是透明的。分層設計的好處是,每一層的功能比較明確,而且修改某一層的實現不會影響其他層。TCP/IP在每層協議中都定義了非常多的不同的協議,比如網際層的協議ICMP、IGMP等,傳輸層的TCP、UDP等。在眾多協議中,最具代表性的協議是TCP和IP,因此,互聯網協議被稱為TCP/IP族(千萬別認為TCP和IP就是互聯網協議的全部)。

IP協議是“Internet Protocol”的簡稱,它是為計算機網絡相互連接進行通信而設計的協議。在IP協議中最重要的就是IP地址,IP地址是用來在網絡上唯一標識計算機主機的地址。互聯網中沒有兩個機器有相同的IP地址,因此它是用來標識一臺網絡主機的。所有的IP地址都是32位長,它用點分十進制法來表示,比如“10.10.30.12”。IP地址指定的不是主機,而是網絡接口設備。因此,一臺主機有兩個網絡接口,那么就會有兩個IP地址。通常情況下,對于一臺普通主機只有一個網絡接口設備,也就只有一個IP地址,比如個人使用的PC通常只有一個IP地址;而對于服務器或者網絡設備(交換機、路由器等)來說,則會有多個網絡接口設備,每個網絡接口設備都會有一個IP地址,那么對于路由器這種網絡設備來說就會有多個IP地址。

IP地址被分為5類,分別是A類、B類、C類、D類和E類。各類IP地址的范圍如表1所示。

表1  各類IP地址的范圍

IP工作在TCP/IP 4層協議的“網際層”,網際層最主要的工作是將數據包進行路由。這里所說IP是一種被路由協議,也就是在進行路由的過程中,IP協議會被路由協議用到。真正進行數據包選路的協議(其實就是路由的算法,數據包如何進行轉發的算法)被稱為路由協議,具體的路由協議有RIP、OSPF、BGP等。對于入門而言,只要了解了IP地址是什么,IP地址的作用是什么即可。

傳輸層主要有兩大協議,分別是TCP協議和UDP協議。

TCP是“Transmission Control Protocol”的簡稱,其意思為傳輸控制協議。TCP是一種面向連接的、可靠的通信協議。TCP協議是IP協議的上層協議,IP服務于TCP。

UDP是“User Datagram Protocol”的簡稱,其意思為用戶數據報協議。UDP是一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務。

傳輸層是為應用層提供服務的,應用層的協議一部分是基于TCP的,比如FTP、HTTP,而一部分是基于UDP的,比如DNS。IP層提供了IP地址用來標識網絡主機,而傳輸層提供了端口用來標識主機中的進程。確定了IP地址和端口號,就確定了網絡上的主機及主機上通信的進程。

傳輸層提供了標識通信進程的端口號。按照協議劃分,端口分為TCP端口和UDP端口,TCP端口和UDP端口各有65536個。對于應用程序而言,一般使用大于1024的端口號,因為小于1024的端口號屬于保留端口。Internet上的很多服務都是用了小于1024的端口號。為了避免沖突,程序員自己編寫的應用程序不要使用小于1024的端口號。同一協議的端口不能沖突,比如Web服務器占用了主機TCP的80端口,那么另外的程序就不可以再使用TCP的80端口。常用的端口號如表2所示。

表2  常用端口號舉例

除了小于1024的端口號外,還有一些比較知名的端口號,比如MS SQL Server的端口號是1433,Windows的遠程桌面端口號是3389等。程序員在編寫自己的網絡應用程序時,要避免與這些常用的端口沖突。

02 面向連接協議與非面向連接協議所使用的函數

1. 面向連接的協議

在面向連接的協議中,兩臺計算機之間在進行數據收發前,必須先在兩者之間建立一個通信信道,以確保兩臺計算機之間存在一條路徑可以互相溝通。在數據傳輸完畢后,切斷這條通信信道。該種方式相當于打電話,用戶在手機上撥10086,當客服人員接聽后,用戶就可以開始通話,通話完畢后就可以掛電話了。

面向連接的協議使用的是TCP,服務器與客戶端建立通信信道所需要的基本Winsock函數如下。

服務器端函數: 

  1. socket()->bind()->listen()->accept()->send()/recv()->closesocket() 

客戶端函數: 

  1. socket()->connet()->send()/recv()->closesocket() 

2. 非面向連接的協議

在非面向連接的協議中,發送端只要直接將要發送的數據傳出即可,不需要理會接收端是否能夠收到數據。而接收端在接收到數據時,也不會響應信息通知發送給發送端。該種方式就相當于寫信,將寫好的信放到信箱中,但是卻不能保證收信人真的能夠收到這封信件。

非面向連接的協議使用的是UDP,服務器與客戶端通信所需要的基本Winsock函數如下:

服務器端函數: 

  1. socket()->bind()->sendto()/recvfrom()->closesocket() 

客戶端函數: 

  1. socket()->sendto()/recvfrom()->closesocket() 

03 Winsock網絡編程知識

Winsock是Windows下網絡編程的基礎,下面介紹Winsock的常用函數。

1. Winsock的初始化與釋放

在使用Winsock相關函數時需要對Winsock庫進行初始化,而在使用完成后需要對Winsock庫進行釋放。完成Winsock庫的初始化和釋放的函數如下。

Winsock庫的初始化函數的定義: 

  1. int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); 

該函數的第1個參數wVersionRequested是需要初始化Winsock庫的版本號。第2個參數lpWSAData是一個指向WSADATA的指針。該函數的返回值為0,說明函數調用成功。如果函數調用失敗,則返回其他值。在程序的開始處調用該初始化函數,在程序中就可以使用Winsock相關的所有API函數。

Winsock庫的釋放函數的定義: 

  1. int WSACleanup (void); 

該函數沒有參數,在程序的結束處直接調用該函數,即可釋放Winsock庫。

初始化與釋放Winsock庫的代碼示例如下: 

  1. WORD wVersionRequested;  
  2. WSADATA wsaData;  
  3. int err;  
  4. wVersionRequested = MAKEWORD( 2, 2 );  
  5. err = WSAStartup( wVersionRequested, &wsaData );  
  6. if ( err != 0 )  
  7.  
  8.   return -1;  
  9.  
  10. if ( LOBYTE( wsaData.wVersion ) != 2 ||  
  11. HIBYTE( wsaData.wVersion ) != 2 )  
  12.  
  13.   WSACleanup( );  
  14.   return -1;  
  15.  
  16. // …… 
  17. WSACleanup();  

2. 套接字的創建與關閉

套接字用于根據指定的協議類型來分配一個套接字描述符。該描述符主要用在客戶端和服務器端進行通信連接,當套接字使用完畢時應該關閉套接字以釋放資源。創建套接字與關閉套接字的函數為socket()和closesocket()。

創建套接字的函數定義如下: 

  1. SOCKET socket(int af, int type, int protocol); 

socket()函數共有3個參數,第1個參數af用來指定地址族,在Windows下可以使用的參數值有多個,但是真正可以使用的只有兩個,分別是AF_INET和PF_INET。這兩個宏在Winsock2.h下的定義是相同的,分別如下: 

  1. #define AF_INET 2 /* internetwork: UDP, TCP, etc. */  
  2. /*  
  3. * Protocol families, same as address families for now.  
  4. */  
  5. #define PF_INET AF_INET 

以上兩個定義都摘自Winsock2.h頭文件。從定義中可以看出,PF_INET和AF_INET是相同的。看PF_INET宏定義上面的注釋,AF表示地址族(Address Family),而PF表示協議族(Protocol Family)。對于Windows來說,兩者相同;對于Unix/Linux來說,兩者是不相同的。一般情況下,在調用socket()函數時應該使用PF_INET,而在設置地址時使用AF_INET。FP_INET上面的那句注釋,同樣也是出自Winsock2.h頭文件中。“Protocol families,same as address families for now.”,也就是說,目前PF和AF是相同的。注釋中說目前是相同的,可能這樣定義是為以后預留的,為了保持良好的兼容性。調用socket()函數時,應該使用PF_INET宏,而盡量避免或不去使用AF_INET宏。

socket()函數的第 2 個參數 type 是指定新套接字描述符的類型。這里可以使用的值通常有3個,分別是 SOCK_STREAM、SOCK_DGRAM 和 SOCK_RAW,分別表示流套接字、數據包套接字和原始協議接口。

socket()函數的第 3 個參數 protocol 用來指定應用程序所使用的通信協議,這里可以選擇使用 IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP 等協議,這個參數的值根據第 2 個參數的值進行選擇。第2 個參數如果使用SOCK_STREAM,那么第3 個參數應該使用IPPROTO_TCP;如果第 3 個參數使用了 SOCK_DGRAM,那么第 3 個參數應該使用 IPPROTO_UDP。為了書寫簡單,如果第 2 個參數是 SOCK_STREAM 或 SOCK_DGRAM,那么第 3 個參數可以默認為 0。如果第 2 個參數指定的是 SOCK_RAW,那么第 3 個參數就必須指定,而不能使用 0 值。

socket()函數調用成功返回值為一個新的套接字描述符,如果調用失敗,則返回 INVALID_SOCKET。調用失敗后,想要知道調用失敗的原因,那么緊接著調用 WSAGetLastError()函數得到錯誤碼。

所有的Winsock函數出錯后,都可以調用WSAGetLastError()函數得到錯誤碼,但是WSAStartup()不能通過WSAGetLastError()得到錯誤碼,因為WSAStartup()未調用成功,不能調用WSAGetLastError()函數。

關閉套接字的函數定義如下: 

  1. int closesocket(SOCKET s); 

closesocket()函數的參數是 socket()函數創建的套接字描述符。

對于WSAStartup()/WSACleanup()和socket()/closesocket()這樣的函數,最好保持成對出現。也就是說,在寫完一個函數時,立刻寫出另外一個函數的調用,以免忘記資源的釋放。

3. 面向連接協議的函數

bind()、listen()、accept()、connect()、send()和recv(),這些函數是常用的面向連接的函數,它們都是Winsock面向連接的最基本的函數。下面介紹幾個函數的使用方法。

通過socket()函數可以創建一個新的套接字描述符,但是它只是一個描述符,它為網絡的一些資源做準備。要真正在網絡上進行通信,需要本地的地址與本地的端口號信息。當然,本地地址與端口號信息要去套接字描述符進行關聯進行綁定。在Winsock函數中,使用bind()函數完成套接字與地址端口信息的綁定。bind()函數的定義如下: 

  1. int bind(SOCKET s, const struct sockaddr FAR *name, int namelen); 

該函數有3個參數,第1個參數s是新創建的套接字描述符,也就是用socket()函數創建的描述符,第2個參數name是一個sockaddr的結構體,提供套接字一個地址和端口信息,第3個參數namelen是sockaddr結構體的大小。

其中第2個參數中的sockaddr結構體定義如下: 

  1. struct sockaddr {  
  2.  u_short sa_family;  
  3.  char sa_data[14]; 
  4.  }; 

該結構體共有16字節,在該結構體之前所使用的結構體為sockaddr_in,該結構體的定義如下: 

  1. struct sockaddr_in {  
  2.  short sin_family;  
  3.  u_short sin_port;  
  4.  struct in_addr sin_addr;  
  5.  char sin_zero[8];  
  6.  }; 

sockaddr結構體是為了保持各個特定協議之間的結構體兼容性而設計的。為bind()函數指定地址和端口時,向sockaddr_in結構體填充相應的內容,而調用函數時應該使用sockaddr結構體。

在sockaddr_in結構體中,還有一個結構體in_addr,該結構體在winsock2.h中的定義如下: 

  1. struct in_addr {  
  2.  union {  
  3.  struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;  
  4.  struct { u_short s_w1,s_w2; } S_un_w;  
  5.  u_long S_addr;  
  6.  } S_un;  
  7. }; 

該結構體中是一個共用體S_un,包含兩個結構體變量和1個u_long類型變量。一般使用的IP地址是使用點分十進制表示的,而in_addr結構體中卻沒有提供用來保存點分十進制表示IP地址的數據類型,這時需要使用轉換函數,把點分十進制表示的IP地址轉換成in_addr結構體可以接受的類型。這里使用的轉換函數是inet_addr(),該函數的定義如下: 

  1. unsigned long inet_addr(const char FAR *cp); 

該函數是將點分十進制表示IP地址轉換成unsigned long類型的數值。該函數的參數cp是指向點分十進制IP地址的字符指針。同時該函數有一個逆函數,是將unsigned long型的數值型IP地址轉換為點分十進制的IP地址字符串,該函數的定義如下: 

  1. char FAR * inet_ntoa(struct in_addr in); 

sockaddr_in 結構體中的 sin_port 表示端口,這個端口需要使用大尾方式字節序存儲(大尾方式和小尾方式是兩種不同的存儲方式)。在 Intel X86 架構下,數值存儲方式默認都是小尾方式字節序,而 TCP/IP 的數值存儲方式都是大尾方式的字節序。為了實現方便的轉換,winsock2.h中提供了方便的函數,即 htons()和 htonl()兩個函數,并且提供了它們的逆函數 ntohs()和 ntohl()。

htons()和 htonl()函數的定義分別如下: 

  1. u_short htons(u_short hostshort);  
  2. u_long htonl(u_long hostlong); 

ntohs()和 ntohl()函數的定義分別如下: 

  1. u_short ntohs(u_short netshort);  
  2. u_long ntohl(u_long netlong); 

這4個函數中,前兩個函數是將主機字節序轉換為網絡字節序(host to network),后兩個函數是將網絡字節序轉換為主機字節序(network to host)。在有些架構系統下,主機字節序和網絡字節序是相同的,那樣轉換函數不進行任何轉換,但是為了代碼的移植性,還是會進行轉換函數的調用。

具體bind()函數的使用方法如下: 

  1. // 創建套接字  
  2. SOCKET sLisent = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  
  3. // 對 sockaddr_in 結構體填充地址、端口等信息  
  4. struct sockaddr_in ServerAddr;  
  5. ServerAddr.sin_family = AF_INET 
  6. ServerAddr.sin_addr.S_un.S_addr = inet_addr("10.10.30.12");  
  7. ServerAddr.sin_port = htons(1234);  
  8. // 綁定套接字與地址信息  
  9. bind(sLisent, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)); 

對于服務器端的地址可以指定為INADDR_ANY宏,表示“任意地址”或“所有地址”。當客戶端發起連接時,服務器操作系統接收到客戶端的連接,根據網絡的配置情況會自動選擇一個IP地址和客戶端進行通信。

當套接字與地址端口信息綁定以后,就需要讓端口進行監聽,當端口處于監聽狀態以后就可以接受其他主機的連接了。監聽端口和接受連接請求的函數分別為listen()和accept()。

監聽端口的函數定義如下: 

  1. int listen(SOCKET s, int backlog); 

該函數有兩個參數,第1個參數s是指定要監聽的套接字描述符,第2個參數backlog是允許進入請求連接隊列的個數,backlog的最大值由系統指定,在winsock2.h中,其最大值由SOMAXCONN表示,該值的定義如下: 

  1. #define SOMAXCONN 0x7fffffff 

接受連接請求的函數定義如下: 

  1. SOCKET accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen); 

該函數從連接請求隊列中獲得連接信息,創建新的套接字描述符,獲取客戶端地址。新創建的套接字用于和客戶端進行通信,在服務器和客戶端通信完成后,該套接字也需要使用closesocket()函數進行關閉,以釋放相應的資源。該函數有3個參數,第1個參數s是處于監聽的套接字描述符,第2個參數addr是一個指向sockaddr結構體的指針,用來返回客戶端的地址信息,第3個參數addrlen是一個指向int型的指針變量,用來傳入sockaddr結構體的大小。

上面介紹的是面向連接的服務器端的函數,完成了一系列服務器應有的基本的動作,具體如下。

① bind()函數將套接字描述符與地址信息進行綁定。

② listen()函數將綁定過套接字描述符置于監聽狀態。

③ accept()函數獲取連接隊列中的連接信息,創建新的套接字描述符,以便與客戶端通信。

面向連接的客戶端只需要完成與服務器的連接這樣一個動作就可以實現和服務器端的通信了。創建套接字描述符后,使用connect()函數就可以完成與服務器的連接。

connect()函數的定義如下: 

  1. int connect(SOCKET s, const struct sockaddr FAR *name, int namelen); 

該函數的作用是將套接字進行連接。該函數有3個參數,第1個參數s表示創建好的套接字描述符,第2個參數name是指向sockaddr結構體的指針,sockaddr結構體中保存了服務器的IP地址和端口號,第3個參數namelen是指定sockaddr結構體的長度。

當客戶端使用connect()函數與服務器連接后,客戶端和服務器就可以進行通信了。通信時主要就是信息的發送和信息的接收。這里介紹的函數有兩個,分別是send()和recv()。

發送函數send()的定義如下: 

  1. int send(SOCKET s, const char FAR *buf, int len, int flags); 

該函數有4個參數,第1個參數s是套接字描述符,該套接字描述符對于服務器端而言,使用的是accept()函數返回的套接字描述符,對于客戶端而言,使用的是socket()函數創建的套接字描述符,第2個參數buf是發送消息的緩沖區,第3個參數len是緩沖區的長度,第4個參數flags通常賦為0值。

接收函數recv()的定義如下: 

  1. int recv(SOCKET s, char FAR *buf, int len, int flags); 

該函數有4個參數。該函數的使用方法與send()函數的使用方法相同,這里不再進行介紹。

從send()和recv()兩個函數的名稱來看分別是發送和接受的意思,但是實際上對于數據的發送和接收依靠的是網絡協議來完成的,send()函數和recv()函數只是完成了將數據從網絡協議所使用的緩沖區中進行拷貝的一個動作。

4. 非面向連接協議的函數

在面向連接的TCP中,服務器端將套接字描述符與地址進行綁定后,需要將端口進行監聽,等待接受客戶端的連接請求,而在客戶端則需要連接服務器,完成這些步驟就可以保證面向連接的TCP的可靠傳輸,在調用connect()函數的過程中也完成了TCP的“三次握手”的過程。非面向連接的UDP協議在開發上基本與面向連接TCP的協議相同。在非面向連接的UDP開發中,服務器端不需要對端口進行監聽,也就不需要等待接受客戶端的連接請求,而客戶端也不需要完成與服務器端的連接。中間的“三次握手”過程也就省略了,這樣UDP相對于TCP來講就顯得不可靠了,但是在效率方面卻要快于TCP。

在非面向連接協議開發中,服務器端不再需要調用listen()、accept()函數,客戶端不再需要調用connect()函數。而服務器和客戶端的通信函數使用sendto()和recvfrom()函數即可。

sendto()函數的定義如下: 

  1. int sendto(  
  2.  SOCKET s,  
  3.  const char FAR *buf,  
  4.  int len,  
  5.  int flags,  
  6.  const struct sockaddr FAR *to,  
  7.  int tolen  
  8. ); 

該函數是來用在UDP通信雙方進行發送數據的函數,該函數有6個參數,第1個參數s是套接字描述符,第2個參數buf是要發送數據的緩沖區,第3個參數len是指定第2個參數的長度,第4個參數通常賦0值,第5個參數to是一個指向sockaddr結構體的指針,這里給出接收消息的地址信息,第6個參數tolen是指定第5個參數的長度。

recvfrom()函數的定義如下: 

  1. int recvfrom(  
  2.  SOCKET s,  
  3.  char FAR* buf,  
  4.  int len,  
  5.  int flags,  
  6.  struct sockaddr FAR *from,  
  7.  int FAR *fromlen  
  8. ); 

該函數是用來在UDP通信雙方進行接收數據的函數。該函數的用法與sendto()相同,這里不再進行介紹。

sendto()函數和recvfrom()函數的功能與send()函數和recv()函數類似,它們都是用于向網絡協議緩沖區進行數據復制的函數,并不是真正的去完成數據的發送和接收的。

04 字節順序

字節序的存在是由于不同架構CPU在訪問數據時所采取的順序不同,在計算機內存中對數值的存儲有一定的標準,而該標準隨著系統架構的不同而不同。了解字節存儲順序對于逆向工程是一項基礎的知識,在動態分析程序的時候,往往需要觀察內存數據的變化情況,如果不了解字節存儲順序,那么可能會迷失在內存的汪洋大海中而無法繼續逆向航行。

1. 字節序基礎

通常情況下,數值在內存中存儲的方式有兩種,一種是大尾方式(大尾字節序就是網絡字節序),另一種是小尾方式。

先來看一個簡單的例子,比如0x01020304這樣一個數值,如果用大尾方式存儲,其存儲方式為01 02 03 04,而用小尾方式存儲則是04 03 02 01。這樣表示也許不直觀,用表格的形式展示其具體的區別,如表3所示。

表3  字節順序的對比表

從表中可以得到如下結論。

大尾存儲方式:內存高位地址存放數據低位字節數據,內存低位地址存放數據高位字節數據;

小尾存儲方式:內存高位地址存放數據高位字節數據,內存低位地址存放數據低位字節數據。

2. 主機字節序與網絡字節序

主機字節序與網絡字節序是相對的概念。

所謂主機字節序,是指主機在存儲數據時的字節順序,主機字節序根據系統架構的不同而不同。通常情況下,Windows操作系統兼容的CPU為小尾方式,而UNIX操作系統所兼容的CPU多為大尾方式。因此,主機字節序并非固定的字節序,需要根據不同的系統架構進行確定。

所謂網絡字節序,是指網絡傳輸相關協議所規定的字節傳輸順序,TCP/IP所使用的字節序為大尾方式。

3. 字節序相關函數

涉及字節序常用的相關函數有htons()、htonl()、ntohs()和ntohl()。這4個函數的定義分別如下: 

  1. u_short htons(u_short hostshort);  
  2. u_long htonl(u_long hostlong);  
  3. u_short ntohs(u_short netshort);  
  4. u_long ntohl(u_long netlong); 

在Windows下,使用以上4個轉換函數會改變值的大小,因為其在內存中的存放方式改變了。如果在UNIX系統下,使用以上4個轉換函數是不會發生任何改變的。無論是何種系統,在進行網絡開始時都需要調用這些函數進行轉換,因為這樣做可以有效的保證在網絡中傳輸的確實是網絡字節序。

4. 編程判斷主機字節序

“編程判斷主機字節序”是很多殺毒軟件公司或者是安全開發職位的一道面試題,因為這題比較基礎。這里給出對于該題目的實現方法。完成該題目有兩種方法,第1種方法是“取值比較法”,第2種方法是“直接轉換比較法”。

方法一:取值比較法

所謂取值比較法,首先定義一個4字節的十六進制數。因為使用調試器查看內存最直觀的就是十六進制值,所以定義十六進制數是一個操作起來比較直觀的方法。而后通過指針方式取出這個十六進制數在“內存”中的某一字節,最后和實際數值中相對應的數進行比較。由于字節序的問題,內存中的某字節與實際數值中對應的字節可能不同,這樣就可以確定字節序了。

代碼如下: 

  1. int main(int argc, char* argv[])  
  2.  
  3.   DWORD dwSmallNum = 0x01020304 
  4.   if ( *(BYTE *)&dwSmallNum == 0x04 )  
  5.   {  
  6.     printf("Small Sequence. \r\n");  
  7.   }  
  8.   else  
  9.   {  
  10.     printf("Big Sequence. \r\n");  
  11.   }  
  12.   return 0;  
  13. }   

以上代碼中,定義了0x01020304這個十六進制數,其在小尾方式內存中的存儲順序為04 03 02 01。取*(BYTE *)&dwSmallNum內存中低地址位的值,如果是小尾方式的話,那么低地址位存儲的值為0x04,如果是大尾方式則為0x01。

方法二:直接轉換比較法

所謂直接轉換比較法,是利用字節序轉換函數將所定義的值進行轉換,然后用轉換后的值和原值進行比較。如果原值與轉換后的值相同,說明為大尾方式,否則為小尾方式。

代碼如下: 

  1. int main(int argc, char* argv[])  
  2.  
  3.   DWORD dwSmallNum = 0x01020304 
  4.   if ( dwSmallNum == htonl(dwSmallNum) )  
  5.   {  
  6.     printf("Big Sequence. \r\n");  
  7.   }  
  8.   else  
  9.   { 
  10.      printf("Small Sequence. \r\n");  
  11.   }  
  12.   return 0;  

這種方法比較直接,如果轉換后的結果與原值相等,就說明是大尾方式,因為轉換后的結果是網絡字節序,網絡字節序等同于大尾方式。

關于字節序的內容大家一定要自行調試體會一下,因為在網絡開發中只需要進行簡單的轉換即可,不需要過多的關心它的細節。而如果是做逆向工程時,在內存中要進行數據的查找時,這時字節序的知識會使用到了。 

 

責任編輯:龐桂玉 來源: 計算機與網絡安全
相關推薦

2021-03-03 12:20:42

網絡安全DLL編程

2021-03-01 11:20:13

網絡安全多線程代碼

2021-03-05 13:46:56

網絡安全遠程線程

2021-02-21 18:19:43

網絡安全網絡安全編程創建進程

2021-02-23 10:20:07

網絡安全進程代碼

2016-10-10 00:18:27

2021-06-18 09:55:09

網絡安全目錄監控

2021-04-26 10:32:38

網絡安全PE編程工具

2021-04-30 18:50:44

網絡安全PE編程添加節區

2021-01-18 10:35:18

網絡安全Windows代碼

2021-02-04 10:50:11

網絡安全非阻塞模Winsock編程

2021-05-12 14:57:13

網絡安全密碼代碼

2021-06-15 11:16:24

網絡安全U盤軟件

2021-04-19 10:26:41

網絡安全PE文件

2021-05-24 11:55:55

網絡安全Windows鉤子函數

2021-06-24 08:37:34

網絡安全內核代碼

2021-04-28 14:35:48

網絡安全PE編程代碼

2021-04-25 21:25:09

網絡安全網絡安全編程PE編程

2021-02-07 10:55:01

網絡安全文件API

2021-03-01 11:38:15

網絡安全進程代碼
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久精品国产99国产 | 亚洲一区二区三区桃乃木香奈 | 精品久久一区 | 中文字幕日韩欧美 | 国产高清性xxxxxxxx | 免费黄色片在线观看 | 99riav3国产精品视频 | 天堂av中文在线 | 国产精品高清在线 | 精品亚洲一区二区 | 日韩在线播放视频 | 91精品国产综合久久精品图片 | 日本免费在线观看视频 | 99免费视频| 日韩在线视频免费观看 | 97色在线观看免费视频 | 亚洲一区二区视频 | 黄色网址在线免费观看 | 精品欧美乱码久久久久久 | 久久精品99久久 | 亚洲精品不卡 | 成人片免费看 | 国产精品3区 | 午夜午夜精品一区二区三区文 | 天天躁日日躁性色aⅴ电影 免费在线观看成年人视频 国产欧美精品 | 国产精品久久久久久av公交车 | 日韩一级欧美一级 | 黄色免费在线观看网址 | 性生活毛片 | 日韩一区二区三区在线 | 久久一二区| 国产真实乱对白精彩久久小说 | 欧美性吧| 黄色大片在线播放 | 亚洲h色 | 五月激情婷婷网 | 91在线观看视频 | 欧美不卡一区二区三区 | 欧美激情一区二区三级高清视频 | 国产欧美精品一区二区 | 亚洲精品乱码久久久久v最新版 |