《C++黑客编程解密》02
2022/8/8 14:22:47
本文主要是介绍《C++黑客编程解密》02,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
网络编程
TCP 服务端函数:
- socket()
- bind()
- listen()
- accept()
- send() / recv()
- closesocket()
TCP 客户端函数:
- socket()
- connect()
- send() / recv()
- closesocket()
UDP 服务端:
- socket()
- bind()
- sendto() / recvfrom()
- closesocket()
UDP 客户端:
- socket()
- sendto() / recvfrom()
- closesocket()
winsock 常用函数
1. Winsock 初始化与释放
使用 Winsock 函数前要初始化,使用后要释放。
// 初始化,返回0成功 int PASCAL FAR WSAStartup( _In_ WORD wVersionRequired, // 需要初始化 Winsock 库的版本号,例如 2.2 _Out_ LPWSADATA lpWSAData // 一个指向 WSADATA 的指针 ); // 释放 int PASCAL FAR WSACleanup(void);
示例:
#include <Windows.h> int main(int argc, char* argv[]) { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { return -1; } if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { WSACleanup(); return -1; } // ... WSACleanup(); return 0; }
启动时可能保存:error LNK2019: 无法解析的外部符号 WSAStartup,函数 main 中引用了该符号
解决方法:向 项目的属性页 -> 链接器 -> 输入 -> 附加依赖项
添加 WS2_32.lib
Psapi.lib
2. 套接字的创建与关闭
// 使用 SOCKET_STREAM 时第三个参数可默认为0,默认使用 IPPROTO_TCP // 使用 SOCKET_DGRAM 时第三个参数可默认为0,默认使用 IPPROTO_UDP // 成功返回套接字,失败返回 INVALID_SOCKET,调用 WSAGetLastError() 获得错误码 SOCKET socket( int af, // PF_INET(首选) AF_INET 地址族和协议组二者windows下相等,linux下不同 int type, // SOCKET_STREAM(流套接字) SOCKET_DGRAM(数据包套接字) SOCKET_RAW(原始协议接口) int protocol // 指定通信协议,IPPROTO_TCP IPPROTO_UDP IPPROTO_ICMP 等。 ); int closesocket(SOCKET s);
3. 面向连接协议的函数
绑定地址和端口:
int PASCAL FAR bind ( _In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR *addr, _In_ int namelen ); // 此结构用于不同协议间兼容,调用函数时将其他结构体转型为这个类型 struct sockaddr { u_short sa_family; /* address family */ char sa_data[14]; /* up to 14 bytes of direct address */ }; // 具体使用的地址 struct sockaddr_in { short sin_family; u_short sin_port; // port 大端方式存储 struct in_addr sin_addr; // ip char sin_zero[8]; }; typedef struct in_addr { union { struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { USHORT s_w1,s_w2; } S_un_w; ULONG S_addr; } S_un; #define s_addr S_un.S_addr /* can be used for most tcp & ip code */ #define s_host S_un.S_un_b.s_b2 // host on imp #define s_net S_un.S_un_b.s_b1 // network #define s_imp S_un.S_un_w.s_w2 // imp #define s_impno S_un.S_un_b.s_b4 // imp # #define s_lh S_un.S_un_b.s_b3 // logical host } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR; // in_addr 没有使用点分十进制保存IP,需要用转换函数 // 点分十进制 转为 unsigned long unsigned long PASCAL FAR inet_addr (_In_z_ const char FAR * cp); // 前一个函数的逆函数 char FAR * PASCAL FAR inet_ntoa (_In_ struct in_addr in); // winsock 中大小端转换 // 主机字节序转为网络字节序 u_short PASCAL FAR htons (_In_ u_short hostshort); u_long PASCAL FAR htonl ( _In_ u_long hostlong); // 网络字节序转为主机字节序 u_short PASCAL FAR ntohs (_In_ u_short netshort); u_long PASCAL FAR ntohl (_In_ u_long netlong);
bind函数使用示例:
SOCKET sLisent = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY; 表示 任意地址/所有地址 ServerAddr.sin_port = htons(1234); bind(sLisent, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr));
用于连接的函数:
int PASCAL FAR listen ( _In_ SOCKET s, _In_ int backlog // 连接队列长度 最大值为 #define SOMAXCONN 0x7fffffff ); SOCKET PASCAL FAR accept ( _In_ SOCKET s, _Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR *addr, // 指向sockaddr的指针,返回客户端地址 _Inout_opt_ int FAR *addrlen // 传入结构体大小 ); int PASCAL FAR connect ( _In_ SOCKET s, _In_reads_bytes_(namelen) const struct sockaddr FAR *name, // 连接目标的地址 _In_ int namelen ); int PASCAL FAR send ( _In_ SOCKET s, _In_reads_bytes_(len) const char FAR * buf, _In_ int len, _In_ int flags // 通常为 0 ); int PASCAL FAR recv ( _In_ SOCKET s, _Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, _In_ int len, _In_ int flags );
4. 非面向连接协议的函数
int PASCAL FAR sendto ( _In_ SOCKET s, _In_reads_bytes_(len) const char FAR * buf, _In_ int len, _In_ int flags, // 一般为0 _In_reads_bytes_opt_(tolen) const struct sockaddr FAR *to, // 目标地址 _In_ int tolen); // 前者长度 int PASCAL FAR recvfrom ( _In_ SOCKET s, _Out_writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf, _In_ int len, _In_ int flags, _Out_writes_bytes_to_opt_(*fromlen, *fromlen) struct sockaddr FAR * from, _Inout_opt_ int FAR * fromlen);
TCP 示例:
服务端:
#include <Windows.h> #include <iostream> #include <stdio.h> int main(int argc, char* argv[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sLisent = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ServerAddr.sin_port = htons(1234); bind(sLisent, (SOCKADDR*) & ServerAddr, sizeof(ServerAddr)); listen(sLisent, SOMAXCONN); SOCKADDR_IN ClientAddr; int nSize = sizeof(ClientAddr); SOCKET sClient = accept(sLisent, (SOCKADDR*) & ClientAddr, &nSize); printf("client ip = %s:%d", inet_ntoa(ClientAddr.sin_addr), ntohs(ClientAddr.sin_port)); char szMsg[MAXBYTE] = { 0 }; strcpy(szMsg, "hello"); send(sClient, szMsg, sizeof(szMsg), 0); recv(sClient, szMsg, sizeof(szMsg), 0); WSACleanup(); return 0; }
客户端:
#include <Windows.h> #include <iostream> #include <stdio.h> int main(int argc, char* argv[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ServerAddr.sin_port = htons(1234); connect(sServer, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)); char szMsg[MAXBYTE] = { 0 }; recv(sServer, szMsg, sizeof(szMsg), 0); send(sServer, szMsg, sizeof(szMsg) + sizeof(char), 0); WSACleanup(); return 0; }
UCP 示例:
服务端:
#include <Windows.h> #include <iostream> #include <stdio.h> int main(int argc, char* argv[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ServerAddr.sin_port = htons(1234); bind(sServer, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)); char szMsg[MAXBYTE] = { 0 }; SOCKADDR_IN ClientAddr; int nSize = sizeof(ClientAddr); recvfrom(sServer, szMsg, MAXBYTE, 0, &ClientAddr, &nSize); sendto(sServer, szMsg, strlen(szMsg) + sizeof(char), 0, (SOCKADDR*) & ClientAddr, nSize); WSACleanup(); return 0; }
客户端:
#include <Windows.h> #include <iostream> #include <stdio.h> int main(int argc, char* argv[]) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SOCKADDR_IN ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ServerAddr.sin_port = htons(1234); bind(sClient, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)); char szMsg[MAXBYTE] = { 0 }; strcpy(szMsg, "hello"); int nSize = sizeof(ServerAddr); sendto(sClient, szMsg, strlen(szMsg) + sizeof(char), 0, (SOCKADDR*)&ServerAddr, nSize); nSize = sizeof(ServerAddr); recvfrom(sClient, szMsg, MAXBYTE, 0, &ServerAddr, &nSize); WSACleanup(); return 0; }
字节顺序
大端方式:内存高位地址放地位数据
小端方式:内存高位地址放高位数据
主机字节序由cpu决定,TCP/IP 协议规定使用大端方式。
// winsock 中大小端转换,内存中字节序改变,windows中数值也改变 // 主机字节序转为网络字节序 u_short PASCAL FAR htons (_In_ u_short hostshort); u_long PASCAL FAR htonl ( _In_ u_long hostlong); // 网络字节序转为主机字节序 u_short PASCAL FAR ntohs (_In_ u_short netshort); u_long PASCAL FAR ntohl (_In_ u_long netlong);
判断主机字节序
方法一:
将 WORD 转为 BYTE类型,比较是等于低字节还是高字节
#include <stdio.h> int main() { DWORD dwSmallNum = 0x01020304; if (*(BYTE*)&dwSmallNum == 0x04) { printf("small sequence"); } else { printf("big sequence"); } }
方法二:
使用 windows 的字节序转换函数
int main() { DWORD dwSmallNum = 0x01020304; if (dwSmallNum == htonl(dwSmallNum)) { printf("big sequence"); } else { printf("small sequence"); } }
非阻塞模式
阻塞模式下Winsock会将线程阻塞,非阻塞模式的Winsock不会发生等待情况。在异步模式下,当一个函数执行后会立刻返回,即使时操作没有完成也会返回;当函数执行完成时,会以某种方式通知应用程序。
设置WInsock工作模式:
// 该函数将套接字绑定到一个窗口,当socket有网络事件发生时,向绑定窗口发送响应消息 int PASCAL FAR WSAAsyncSelect( _In_ SOCKET s, // 要改为非阻塞模式的套接字 _In_ HWND hWnd, // 发生网络事件时接收消息的窗口 _In_ u_int wMsg, // 发送的自定义消息,比如 WM_USER+1 _In_ long lEvent // 通知码 ); /* 常用通知码: FD_READ 收到数据包 FD_ACCEPT 监听中套接字有连接请求 FD_CONNECT 成功连接到对方 FD_CLOSE 连接被关闭 */
原始套接字开发
当socket()函数第2个参数为 SOCK_RAW
时使用的是原始套接字类型,第3个参数可取 IPPROTO_IP
IPPROTO_ICMP
IPPROTO_TCP
IPPROTO
IPPROTO_RAW
。使用前4种类型,当发送数据时系统自动添加上IP首部并设置IP首部的上层协议字段(如果有 IP_HDRINCL 选项则不自动添加IP首部);当接收到数据时,系统不会讲IP首部移除,需要自行处理。如果使用 IPPROTO_RAW ,那么系统讲数据包直接送到网络层发送数据,并要程序自己构造IP首部字段。
Ping 命令构造:
依赖ICMP协议,ICMP位于IP协议之上。
ICMP协议的类型码和代码根据不同情况取不同值。Ping命令类型码用到两个值0和8,代码取值都为0。
类型码为0,代码为0,表示回显应答;
类型码为8,代码为0,表示请求回显。
自定义 IMCP 数据结构如下:
struct icmp_header { unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 unsigned short icmp_id; // 标识此请求的ID,通常用进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳 };
例子:
icmp
#include <Windows.h> #include <stdio.h> // 不加此行会报错 #pragma comment(lib,"ws2_32.lib") #define ICMP_HEADER_SIZE sizeof(icmp_header) #define ICMP_ECHO_REQUEST 0x08 #define ICMP_ECHO_REPLY 0x00 struct icmp_header { unsigned char icmp_type; // 消息类型 unsigned char icmp_code; // 代码 unsigned short icmp_checksum; // 校验和 unsigned short icmp_id; // 标识此请求的ID,通常用进程ID unsigned short icmp_sequence; // 序列号 unsigned long icmp_timestamp; // 时间戳 }; // 计算校验和 unsigned short chsum(struct icmp_header* picmp, int len) { long sum = 0; unsigned short* pusicmp = (unsigned short*)picmp; while (len > 1) { sum += *(pusicmp++); if (sum & 0x80000000) { sum = (sum & 0xffff) + (sum >> 16); } len -= 2; } if (len) { sum += (unsigned short)*(unsigned char*)pusicmp; } while (sum >> 16) { sum = (sum & 0xffff) + (sum >> 16); } return (unsigned short)~sum; } BOOL MyPing(char* szDestIp) { BOOL bRet = TRUE; WSADATA wsaData; int nTimeOut = 1000; char szBuff[ICMP_HEADER_SIZE + 32] = { 0 }; icmp_header* pIcmp = (icmp_header*)szBuff; char icmp_data[32] = { 0 }; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); // 设置超时 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char const*)&nTimeOut, sizeof(nTimeOut)); // 设置目的地 sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_addr.S_un.S_addr = inet_addr(szDestIp); dest_addr.sin_port = htons(0); // 构造ICMP封包 pIcmp->icmp_type = ICMP_ECHO_REQUEST; pIcmp->icmp_code = 0; pIcmp->icmp_id = GetCurrentProcessId(); pIcmp->icmp_sequence = 0; pIcmp->icmp_timestamp = 0; pIcmp->icmp_checksum = 0; // 拷贝数据,这里数据可以任意 memcpy((szBuff + ICMP_HEADER_SIZE), "abcoerijeriiiiiiiisdijfasrfnjedr", 32); // 计算校验和 pIcmp->icmp_checksum = chsum((struct icmp_header*)szBuff, sizeof(szBuff)); sockaddr_in from_addr; char szRecvBuff[1024]; int nLen = sizeof(from_addr); sendto(s, szBuff, sizeof(szBuff), 0, (SOCKADDR*)&dest_addr, sizeof(SOCKADDR)); recvfrom(s, szRecvBuff, MAXBYTE, 0, (SOCKADDR*)&from_addr, &nLen); if (lstrcmp(inet_ntoa(from_addr.sin_addr), szDestIp)) { bRet = FALSE; } else { struct icmp_header* pIcmp1 = (icmp_header*)(szRecvBuff + 20); printf("%s\n", inet_ntoa(from_addr.sin_addr)); } return bRet; } int main() { MyPing((char *)"127.0.0.1"); }
这篇关于《C++黑客编程解密》02的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-03-28pytorch imagefolder
- 2024-03-28如何使用web of science
- 2024-03-28Pandas error: AttributeError - 'new_block' not found
- 2024-03-28typeerror: cannot convert expression to float
- 2024-03-28yuzu msvcp140_atomic_wait.dll
- 2024-03-28pthread_mutex_unlock
- 2024-03-28create ami
- 2024-03-28conda install langchain
- 2024-03-28pycharm no interpreter
- 2024-03-28Elasticsearch - kibana画图