Chúng tôi viết một sniffer trên c. Chúng tôi đang viết một trình thám thính đơn giản cho Windows. Mô tả cấu trúc gói IP

Trong bài viết này, chúng ta sẽ xem xét việc tạo một trình thám thính đơn giản cho hệ điều hành Windows.
Bất cứ ai quan tâm, chào mừng đến với mèo.

Giới thiệu

Mục tiêu: viết chương trình sẽ nắm bắt lưu lượng mạng (Ethernet, WiFi) được truyền qua giao thức IP.
Cơ sở: Visual Studio 2005 trở lên.
Cách tiếp cận được mô tả ở đây không thuộc về cá nhân tác giả và được sử dụng thành công trong nhiều chương trình thương mại cũng như hoàn toàn miễn phí (xin chào, GPL).
Công việc này chủ yếu dành cho những người mới bắt đầu lập trình mạng, tuy nhiên, những người có ít nhất kiến ​​​​thức cơ bản về lĩnh vực socket nói chung và socket Windows nói riêng. Ở đây tôi sẽ thường viết những điều nổi tiếng, vì chủ đề rất cụ thể, nếu bỏ lỡ điều gì đó, đầu tôi sẽ rối tung.

Tôi hy vọng bạn thấy nó thú vị.

Lý thuyết (không bắt buộc đọc nhưng khuyến khích)

Hiện tại, phần lớn các mạng thông tin hiện đại đều dựa trên nền tảng của chồng giao thức TCP/IP. Ngăn xếp giao thức TCP/IP (Giao thức điều khiển truyền/Giao thức Internet) là tên gọi chung cho các giao thức mạng ở các cấp độ khác nhau được sử dụng trong mạng. Trong bài viết này, chúng ta sẽ chủ yếu quan tâm đến giao thức IP - một giao thức mạng định tuyến được sử dụng để phân phối dữ liệu không bảo đảm được chia thành cái gọi là gói (thuật ngữ chính xác hơn là datagram) từ nút mạng này sang nút mạng khác.
Mối quan tâm đặc biệt của chúng tôi là các gói IP được thiết kế để truyền tải thông tin. Đây là mức khá cao của mô hình dữ liệu mạng OSI, khi bạn có thể tách mình khỏi thiết bị và phương tiện truyền dữ liệu, chỉ hoạt động với biểu diễn logic.
Hoàn toàn hợp lý khi sớm hay muộn các công cụ chặn, giám sát, ghi lại và phân tích lưu lượng mạng đều phải xuất hiện. Những công cụ như vậy thường được gọi là máy phân tích lưu lượng, máy phân tích gói hoặc sniffers (từ tiếng Anh sang sniff - đánh hơi). Đây là bộ phân tích lưu lượng mạng, một chương trình hoặc thiết bị phần cứng-phần mềm được thiết kế để chặn và sau đó phân tích hoặc chỉ phân tích lưu lượng mạng dành cho các nút khác.

Thực hành (đàm thoại thực chất)

Hiện nay có khá nhiều phần mềm được tạo ra để lắng nghe giao thông. Nổi tiếng nhất trong số đó: Wireshark. Đương nhiên, mục tiêu không phải là giành lấy vòng nguyệt quế của anh ấy - chúng tôi quan tâm đến nhiệm vụ chặn lưu lượng truy cập chỉ bằng cách “lắng nghe” giao diện mạng. Điều quan trọng là phải hiểu rằng chúng tôi sẽ không hack và chặn người lạ giao thông. Chúng tôi chỉ cần xem và phân tích lưu lượng truy cập đi qua máy chủ của mình.

Tại sao điều này có thể cần thiết:

  1. Xem lưu lượng truy cập hiện tại thông qua kết nối mạng (đến/đi/tổng).
  2. Chuyển hướng lưu lượng truy cập để phân tích tiếp theo đến máy chủ khác.
  3. Về mặt lý thuyết, bạn có thể thử sử dụng nó để hack mạng WiFi (chúng ta sẽ không làm điều đó phải không?).
Không giống như Wireshark, dựa trên thư viện libpcap/WinPcap, trình phân tích của chúng tôi sẽ không sử dụng trình điều khiển này. Hơn nữa, chúng tôi sẽ không có trình điều khiển nào cả và chúng tôi sẽ không viết NDIS của riêng mình (ôi thật kinh khủng!). Bạn có thể đọc về điều này trong. Anh ta sẽ chỉ đơn giản là một người quan sát thụ động, sử dụng chỉ một Thư viện WinSock. Sử dụng driver trong trường hợp này là không cần thiết.

Làm sao vậy? Rất đơn giản.
Bước quan trọng trong việc biến một ứng dụng mạng đơn giản thành một máy phân tích mạng là chuyển giao diện mạng sang chế độ không liên tục, chế độ này sẽ cho phép nó nhận các gói được gửi đến các giao diện khác trên mạng. Chế độ này buộc card mạng phải chấp nhận tất cả các khung, bất kể chúng được gửi đến ai trên mạng.

Bắt đầu với Windows 2000 (NT 5.0), việc tạo một chương trình nghe một đoạn mạng trở nên rất dễ dàng, bởi vì trình điều khiển mạng của nó cho phép bạn đặt ổ cắm để nhận tất cả các gói.

Kích hoạt chế độ lăng nhăng
cờ dài = 1; ổ cắm SOCKET; #define SIO_RCVALL 0x98000001 ioctlsocket(socket, SIO_RCVALL, &RS_Flag);
Chương trình của chúng tôi hoạt động trên các gói IP và sử dụng thư viện Windows Sockets phiên bản 2.2 và các ổ cắm thô. Để có quyền truy cập trực tiếp vào gói IP, ổ cắm phải được tạo như sau:
Tạo một ổ cắm thô
s = ổ cắm(AF_INET, SOCK_RAW, IPPROTO_IP);
Ở đây thay vì một hằng số SOCK_STREAM(giao thức TCP) hoặc SOCK_DGRAM(giao thức UDP), chúng tôi sử dụng giá trị SOCK_RAW. Nói chung, làm việc với các socket thô không chỉ thú vị từ quan điểm nắm bắt lưu lượng truy cập. Trên thực tế, chúng tôi có toàn quyền kiểm soát việc hình thành gói hàng. Hay đúng hơn là chúng tôi tạo nó theo cách thủ công, ví dụ như cho phép gửi một gói ICMP cụ thể...

Hãy tiếp tục. Được biết, gói IP bao gồm tiêu đề, thông tin dịch vụ và trên thực tế là dữ liệu. Tôi khuyên bạn nên xem ở đây để làm mới kiến ​​​​thức của bạn. Hãy mô tả tiêu đề IP dưới dạng cấu trúc (nhờ bài viết xuất sắc trên RSDN):

Mô tả cấu trúc gói IP
typedef struct _IPHeader ( unsigned char ver_len; // phiên bản tiêu đề và độ dài unsigned char tos; // loại dịch vụ unsigned short short; // độ dài của toàn bộ gói unsigned short id; // Id unsigned short flgs_offset; // cờ và offset không dấu char ttl ; // trọn đời giao thức unsigned char; // giao thức unsigned long src; // địa chỉ IP của người gửi unsigned long dest; // địa chỉ IP đích unsigned short *params; 320 bit) unsigned char *data; 65535 octet) )IPHeader;
Chức năng chính của thuật toán nghe sẽ như thế này:
Chức năng chụp gói đơn
IPHeader* RS_Sniff() ( IPHeader *hdr; int count = 0; count = recv(RS_SSocket, (char*)&RS_Buffer, sizeof(RS_Buffer), 0); if (count >= sizeof(IPHeader)) ( hdr = (LPIPHeader )malloc(MAX_PACKET_SIZE); memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE); RS_UpdateNetStat(count, hdr) nếu không thì trả về 0;
Mọi thứ ở đây đều đơn giản: chúng tôi nhận được một phần dữ liệu bằng chức năng ổ cắm tiêu chuẩn recv, sau đó sao chép chúng vào một cấu trúc như Tiêu đề IP.
Và cuối cùng, chúng tôi bắt đầu một vòng lặp chụp gói vô tận:
Hãy nắm bắt tất cả các gói đến giao diện mạng của chúng tôi
while (true) ( ​​​​IPHeader* hdr = RS_Sniff(); // xử lý gói IP if (hdr) ( // in tiêu đề trong bảng điều khiển) )
Một chút lạc đề
Ở đây và bên dưới, tác giả đã tạo tiền tố RS_ (từ Raw Sockets) cho một số hàm và biến quan trọng. Tôi đã thực hiện dự án này cách đây 3-4 năm và tôi có một ý tưởng điên rồ là viết một thư viện chính thức để làm việc với các socket thô. Như thường lệ, sau khi đạt được một số kết quả quan trọng (đối với tác giả), sự nhiệt tình giảm dần và vấn đề không đi xa hơn một ví dụ đào tạo.

Về nguyên tắc, bạn có thể đi xa hơn và mô tả tiêu đề của tất cả các giao thức tiếp theo nằm ở trên. Để làm điều này, bạn cần phân tích trường giao thức trong cấu trúc Tiêu đề IP. Hãy xem mã ví dụ (vâng, cần có một công tắc, chết tiệt!), trong đó tiêu đề được tô màu tùy thuộc vào giao thức mà gói đã gói gọn trong IP:

/* * Đánh dấu một gói bằng màu */ void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0) ( if (h->xsum) SetConsoleTextColor(0x17); // nếu gói không trống. SetConsoleTextColor(0x07) ; // gói trống if (haddr == h->src) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_RED | FOREGROUND_INTENSITY); // gói "gốc" cho return ) else if (haddr == h->dest ) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_GREEN | FOREGROUND_INTENSITY); // "native" nhận gói ) if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP) ( SetConsoleTextColor (0x70) ; // Gói ICMP ) else if(h->protocol == PROT_IP || h->protocol == 115) ( SetConsoleTextColor(0x4F); // Gói IP-in-IP, L2TP ) else if(h - >giao thức == 53 || h->giao thức == 56) ( SetConsoleTextColor(0x4C); // TLS, IP có mã hóa ) if(whost == h->dest || whost == h->src) ( SetConsoleTextColor (0x0A);

Tuy nhiên, điều này vượt xa phạm vi của bài viết này. Đối với ví dụ đào tạo của chúng tôi, sẽ đủ để xem địa chỉ IP của máy chủ lưu lượng truy cập đến từ đâu và đến đâu, đồng thời tính toán lượng lưu lượng của nó trên một đơn vị thời gian (chương trình đã hoàn thành nằm trong kho lưu trữ ở cuối bài viết) .

Để hiển thị dữ liệu tiêu đề IP, bạn phải triển khai chức năng chuyển đổi tiêu đề (nhưng không phải dữ liệu) của datagram thành chuỗi. Để làm ví dụ về việc triển khai, chúng tôi có thể cung cấp tùy chọn sau:

Chuyển đổi tiêu đề IP thành chuỗi
inline char* iph2str(IPHeader *iph) ( const int BUF_SIZE = 1024; char *r = (char*)malloc(BUF_SIZE); memset((void*)r, 0, BUF_SIZE); sprintf(r, "ver=% d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s", BYTE_H (iph->ver_len), BYTE_L(iph->ver_len)*4, iph->tos, ntohs(iph->length), ntohs(iph->id), IP_FLAGS(ntohs(iph->flgs_offset)), IP_OFFSET (ntohs(iph->flgs_offset)), iph->ttl, iph->giao thức, ntohs(iph->xsum), nethost2str(iph->src), nethost2str(iph->dest));
Dựa trên thông tin cơ bản được cung cấp ở trên, chúng tôi có được chương trình nhỏ này (tên đáng sợ ss, viết tắt của trình thám thính đơn giản), thực hiện việc nghe lưu lượng IP cục bộ. Giao diện của nó được hiển thị dưới đây trong hình.

Tôi cung cấp mã nguồn và mã nhị phân như cách đây vài năm. Bây giờ tôi rất sợ khi nhìn vào nó, tuy nhiên, nó khá dễ đọc (tất nhiên, bạn không thể tự tin như vậy). Ngay cả Visual Studio Express 2005 cũng đủ để biên dịch.

Những gì chúng tôi đã kết thúc với:

  • Trình thám thính hoạt động ở chế độ người dùng nhưng yêu cầu quyền quản trị viên.
  • Các gói không được lọc và được hiển thị nguyên trạng (bạn có thể thêm các bộ lọc tùy chỉnh - Tôi khuyên bạn nên xem chi tiết chủ đề này trong bài viết tiếp theo nếu bạn quan tâm).
  • Lưu lượng truy cập WiFi cũng bị ghi lại (tất cả phụ thuộc vào kiểu chip cụ thể, nó có thể không hoạt động với bạn, giống như nó đã làm với tôi vài năm trước), mặc dù có AirPcap, có thể thực hiện điều này một cách tuyệt vời nhưng lại tốn tiền.
  • Toàn bộ luồng datagram được ghi vào một tập tin (xem kho lưu trữ đính kèm ở cuối bài viết).
  • Chương trình hoạt động như một máy chủ trên cổng 2000. Bạn có thể kết nối với máy chủ bằng tiện ích telnet và giám sát luồng lưu lượng. Số lượng kết nối được giới hạn ở hai mươi (mã không phải của tôi, tôi tìm thấy nó trên Internet và sử dụng nó để thử nghiệm; tôi đã không xóa nó - thật đáng tiếc)
Cảm ơn sự quan tâm của các bạn, tôi xin chúc mừng cư dân Khabrovsk và Khabrovka cũng như tất cả mọi người, Giáng sinh vui vẻ!

Trong bài viết này, chúng ta sẽ xem xét việc tạo một trình thám thính đơn giản cho hệ điều hành Windows.
Bất cứ ai quan tâm, chào mừng đến với mèo.

Giới thiệu

Mục tiêu: viết chương trình sẽ nắm bắt lưu lượng mạng (Ethernet, WiFi) được truyền qua giao thức IP.
Cơ sở: Visual Studio 2005 trở lên.
Cách tiếp cận được mô tả ở đây không thuộc về cá nhân tác giả và được sử dụng thành công trong nhiều chương trình thương mại cũng như hoàn toàn miễn phí (xin chào, GPL).
Công việc này chủ yếu dành cho những người mới bắt đầu lập trình mạng, tuy nhiên, những người có ít nhất kiến ​​​​thức cơ bản về lĩnh vực socket nói chung và socket Windows nói riêng. Ở đây tôi sẽ thường viết những điều nổi tiếng, vì chủ đề rất cụ thể, nếu bỏ lỡ điều gì đó, đầu tôi sẽ rối tung.

Tôi hy vọng bạn thấy nó thú vị.

Lý thuyết (không bắt buộc đọc nhưng khuyến khích)

Hiện tại, phần lớn các mạng thông tin hiện đại đều dựa trên nền tảng của chồng giao thức TCP/IP. Ngăn xếp giao thức TCP/IP (Giao thức điều khiển truyền/Giao thức Internet) là tên gọi chung cho các giao thức mạng ở các cấp độ khác nhau được sử dụng trong mạng. Trong bài viết này, chúng ta sẽ chủ yếu quan tâm đến giao thức IP - một giao thức mạng định tuyến được sử dụng để phân phối dữ liệu không bảo đảm được chia thành cái gọi là gói (thuật ngữ chính xác hơn là datagram) từ nút mạng này sang nút mạng khác.
Mối quan tâm đặc biệt của chúng tôi là các gói IP được thiết kế để truyền tải thông tin. Đây là mức khá cao của mô hình dữ liệu mạng OSI, khi bạn có thể tách mình khỏi thiết bị và phương tiện truyền dữ liệu, chỉ hoạt động với biểu diễn logic.
Hoàn toàn hợp lý khi sớm hay muộn các công cụ chặn, giám sát, ghi lại và phân tích lưu lượng mạng đều phải xuất hiện. Những công cụ như vậy thường được gọi là máy phân tích lưu lượng, máy phân tích gói hoặc sniffers (từ tiếng Anh sang sniff - đánh hơi). Đây là bộ phân tích lưu lượng mạng, một chương trình hoặc thiết bị phần cứng-phần mềm được thiết kế để chặn và sau đó phân tích hoặc chỉ phân tích lưu lượng mạng dành cho các nút khác.

Thực hành (đàm thoại thực chất)

Hiện nay có khá nhiều phần mềm được tạo ra để lắng nghe giao thông. Nổi tiếng nhất trong số đó: Wireshark. Đương nhiên, mục tiêu không phải là giành lấy vòng nguyệt quế của anh ấy - chúng tôi quan tâm đến nhiệm vụ chặn lưu lượng truy cập chỉ bằng cách “lắng nghe” giao diện mạng. Điều quan trọng là phải hiểu rằng chúng tôi sẽ không hack và chặn người lạ giao thông. Chúng tôi chỉ cần xem và phân tích lưu lượng truy cập đi qua máy chủ của mình.

Tại sao điều này có thể cần thiết:

  1. Xem lưu lượng truy cập hiện tại thông qua kết nối mạng (đến/đi/tổng).
  2. Chuyển hướng lưu lượng truy cập để phân tích tiếp theo đến máy chủ khác.
  3. Về mặt lý thuyết, bạn có thể thử sử dụng nó để hack mạng WiFi (chúng ta sẽ không làm điều đó phải không?).

Không giống như Wireshark, dựa trên thư viện libpcap/WinPcap, trình phân tích của chúng tôi sẽ không sử dụng trình điều khiển này. Hơn nữa, chúng tôi sẽ không có trình điều khiển nào cả và chúng tôi sẽ không viết NDIS của riêng mình (ôi thật kinh khủng!). Bạn có thể đọc về điều này trong chủ đề này. Anh ta sẽ chỉ đơn giản là một người quan sát thụ động, sử dụng chỉ một Thư viện WinSock. Sử dụng driver trong trường hợp này là không cần thiết.

Làm sao vậy? Rất đơn giản.
Bước quan trọng trong việc biến một ứng dụng mạng đơn giản thành một máy phân tích mạng là chuyển giao diện mạng sang chế độ không liên tục, chế độ này sẽ cho phép nó nhận các gói được gửi đến các giao diện khác trên mạng. Chế độ này buộc card mạng phải chấp nhận tất cả các khung, bất kể chúng được gửi đến ai trên mạng.

Bắt đầu với Windows 2000 (NT 5.0), việc tạo một chương trình nghe một đoạn mạng trở nên rất dễ dàng, bởi vì trình điều khiển mạng của nó cho phép bạn đặt ổ cắm để nhận tất cả các gói.

Kích hoạt chế độ lăng nhăng
cờ dài = 1; ổ cắm SOCKET; #define SIO_RCVALL 0x98000001 ioctlsocket(socket, SIO_RCVALL, &RS_Flag);

Chương trình của chúng tôi hoạt động trên các gói IP và sử dụng thư viện Windows Sockets phiên bản 2.2 và các ổ cắm thô. Để có quyền truy cập trực tiếp vào gói IP, ổ cắm phải được tạo như sau:

Tạo một ổ cắm thô
s = ổ cắm(AF_INET, SOCK_RAW, IPPROTO_IP);

Ở đây thay vì một hằng số SOCK_STREAM(giao thức TCP) hoặc SOCK_DGRAM(giao thức UDP), chúng tôi sử dụng giá trị SOCK_RAW. Nói chung, làm việc với các socket thô không chỉ thú vị từ quan điểm nắm bắt lưu lượng truy cập. Trên thực tế, chúng tôi có toàn quyền kiểm soát việc hình thành gói hàng. Hay đúng hơn là chúng tôi tạo nó theo cách thủ công, ví dụ như cho phép gửi một gói ICMP cụ thể...

Hãy tiếp tục. Được biết, gói IP bao gồm tiêu đề, thông tin dịch vụ và trên thực tế là dữ liệu. Tôi khuyên bạn nên xem ở đây để làm mới kiến ​​​​thức của bạn. Hãy mô tả tiêu đề IP dưới dạng cấu trúc (nhờ bài viết xuất sắc trên RSDN):

Mô tả cấu trúc gói IP
typedef struct _IPHeader ( unsigned char ver_len; // phiên bản tiêu đề và độ dài unsigned char tos; // loại dịch vụ unsigned short short; // độ dài của toàn bộ gói unsigned short id; // Id unsigned short flgs_offset; // cờ và offset không dấu char ttl ; // trọn đời giao thức unsigned char; // giao thức unsigned long src; // địa chỉ IP của người gửi unsigned long dest; // địa chỉ IP đích unsigned short *params; 320 bit) unsigned char *data; 65535 octet) )IPHeader;

Chức năng chính của thuật toán nghe sẽ như thế này:

Chức năng chụp gói đơn
IPHeader* RS_Sniff() ( IPHeader *hdr; int count = 0; count = recv(RS_SSocket, (char*)&RS_Buffer, sizeof(RS_Buffer), 0); if (count >= sizeof(IPHeader)) ( hdr = (LPIPHeader )malloc(MAX_PACKET_SIZE); memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE); RS_UpdateNetStat(count, hdr) nếu không thì trả về 0;

Mọi thứ ở đây đều đơn giản: chúng tôi nhận được một phần dữ liệu bằng chức năng ổ cắm tiêu chuẩn recv, sau đó sao chép chúng vào một cấu trúc như Tiêu đề IP.
Và cuối cùng, chúng tôi bắt đầu một vòng lặp chụp gói vô tận:

Hãy nắm bắt tất cả các gói đến giao diện mạng của chúng tôi
while (true) ( ​​​​IPHeader* hdr = RS_Sniff(); // xử lý gói IP if (hdr) ( // in tiêu đề trong bảng điều khiển) )
Một chút lạc đề

Ở đây và bên dưới, tác giả đã tạo tiền tố RS_ (từ Raw Sockets) cho một số hàm và biến quan trọng. Tôi đã thực hiện dự án này cách đây 3-4 năm và tôi có một ý tưởng điên rồ là viết một thư viện chính thức để làm việc với các socket thô. Như thường lệ, sau khi đạt được một số kết quả quan trọng (đối với tác giả), sự nhiệt tình giảm dần và vấn đề không đi xa hơn một ví dụ đào tạo.

Về nguyên tắc, bạn có thể đi xa hơn và mô tả tiêu đề của tất cả các giao thức tiếp theo nằm ở trên. Để làm điều này, bạn cần phân tích trường giao thức trong cấu trúc Tiêu đề IP. Hãy xem mã ví dụ (vâng, cần có một công tắc, chết tiệt!), trong đó tiêu đề được tô màu tùy thuộc vào giao thức mà gói đã gói gọn trong IP:

/* * Đánh dấu một gói bằng màu */ void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0) ( if (h->xsum) SetConsoleTextColor(0x17); // nếu gói không trống. SetConsoleTextColor(0x07) ; // gói trống if (haddr == h->src) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_RED | FOREGROUND_INTENSITY); // gói "gốc" cho return ) else if (haddr == h->dest ) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_GREEN | FOREGROUND_INTENSITY); // "native" nhận gói ) if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP) ( SetConsoleTextColor (0x70) ; // Gói ICMP ) else if(h->protocol == PROT_IP || h->protocol == 115) ( SetConsoleTextColor(0x4F); // Gói IP-in-IP, L2TP ) else if(h - >giao thức == 53 || h->giao thức == 56) ( SetConsoleTextColor(0x4C); // TLS, IP có mã hóa ) if(whost == h->dest || whost == h->src) ( SetConsoleTextColor (0x0A);

Tuy nhiên, điều này vượt xa phạm vi của bài viết này. Đối với ví dụ đào tạo của chúng tôi, sẽ đủ để xem địa chỉ IP của máy chủ lưu lượng truy cập đến từ đâu và đến đâu, đồng thời tính toán lượng lưu lượng của nó trên một đơn vị thời gian (chương trình đã hoàn thành nằm trong kho lưu trữ ở cuối bài viết) .

Để hiển thị dữ liệu tiêu đề IP, bạn phải triển khai chức năng chuyển đổi tiêu đề (nhưng không phải dữ liệu) của datagram thành chuỗi. Để làm ví dụ về việc triển khai, chúng tôi có thể cung cấp tùy chọn sau:

Chuyển đổi tiêu đề IP thành chuỗi
inline char* iph2str(IPHeader *iph) ( const int BUF_SIZE = 1024; char *r = (char*)malloc(BUF_SIZE); memset((void*)r, 0, BUF_SIZE); sprintf(r, "ver=% d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s", BYTE_H (iph->ver_len), BYTE_L(iph->ver_len)*4, iph->tos, ntohs(iph->length), ntohs(iph->id), IP_FLAGS(ntohs(iph->flgs_offset)), IP_OFFSET (ntohs(iph->flgs_offset)), iph->ttl, iph->giao thức, ntohs(iph->xsum), nethost2str(iph->src), nethost2str(iph->dest));

Dựa trên thông tin cơ bản được cung cấp ở trên, chúng tôi có được chương trình nhỏ này (tên đáng sợ ss, viết tắt của trình thám thính đơn giản), thực hiện việc nghe lưu lượng IP cục bộ. Giao diện của nó được hiển thị dưới đây trong hình.

Tôi cung cấp mã nguồn và mã nhị phân như cách đây vài năm. Bây giờ tôi rất sợ khi nhìn vào nó, tuy nhiên, nó khá dễ đọc (tất nhiên, bạn không thể tự tin như vậy). Ngay cả Visual Studio Express 2005 cũng đủ để biên dịch.

Những gì chúng tôi đã kết thúc với:

  • Trình thám thính hoạt động ở chế độ người dùng nhưng yêu cầu quyền quản trị viên.
  • Các gói không được lọc và được hiển thị nguyên trạng (bạn có thể thêm các bộ lọc tùy chỉnh - Tôi khuyên bạn nên xem chi tiết chủ đề này trong bài viết tiếp theo nếu bạn quan tâm).
  • Lưu lượng truy cập WiFi cũng bị ghi lại (tất cả phụ thuộc vào kiểu chip cụ thể, nó có thể không hoạt động với bạn, giống như nó đã làm với tôi vài năm trước), mặc dù có AirPcap, có thể thực hiện điều này một cách tuyệt vời nhưng lại tốn tiền.
  • Toàn bộ luồng datagram được ghi vào một tập tin (xem kho lưu trữ đính kèm ở cuối bài viết).
  • Chương trình hoạt động như một máy chủ trên cổng 2000. Bạn có thể kết nối với máy chủ bằng tiện ích telnet và giám sát luồng lưu lượng. Số lượng kết nối được giới hạn ở hai mươi (mã không phải của tôi, tôi tìm thấy nó trên Internet và sử dụng nó để thử nghiệm; tôi đã không xóa nó - thật đáng tiếc)

Cảm ơn bạn đã quan tâm, chúc bạn và mọi người một Giáng sinh vui vẻ!

Chào buổi chiều Bằng cách nào đó, một vấn đề đã nảy sinh tại nơi làm việc - có một thiết bị hoạt động thông qua I2C và cần phải hiểu giao thức của nó. Do đó, chúng ta cần một sniffer cho giao diện I2C, nó sẽ xuất mọi thứ đến và đi qua I2C tới cổng UART, sau đó qua bộ chuyển đổi sang cổng COM của máy tính.

Bắt đầu

Tôi chỉ có một vài chiếc Atmeg8 và tôi quyết định tại sao không sử dụng chúng. Tiếp theo, câu hỏi về mạch đánh hơi nảy sinh.

Có 2 lựa chọn - bật thiết bị đánh hơi song song hoặc ở dạng mạch hở. Rõ ràng, tùy chọn đầu tiên trông đơn giản hơn nhiều, nhưng trên thực tế, nó hoàn toàn sai. Nhưng điều đầu tiên trước tiên.

Nói ngắn gọn về giao diện. I2C (TWI trong Atmel) sử dụng hai dây - SCL và SDA. Cái đầu tiên chịu trách nhiệm bấm giờ tín hiệu, cái thứ hai chịu trách nhiệm truyền thông tin trực tiếp. Giao diện cũng có trạng thái START và STOP.

Vì vậy, suy nghĩ đầu tiên của tôi là lấy một đầu dò và một mặt kết nối nó với chân ngắt bên ngoài trên atmega8, mặt khác với đường SDA và bắt cạnh đầu và xác định 0 hoặc 1 theo thời gian đã trôi qua Rõ ràng là điều này lẽ ra đã hoạt động rất kém vì tín hiệu STOP không được xử lý chính xác.

Ý tưởng thứ hai là làm điều tương tự, nhưng bắt được sự gián đoạn trên đường SCL và đọc đường SDA được kết nối với chân kỹ thuật số thông thường bằng cách sử dụng sự gián đoạn. Ở đây mọi thứ có vẻ khả thi hơn, ngoại trừ trạng thái STOP tương tự, nhưng tôi quyết định thử lắp ráp nó trên một bảng mạch và xem điều gì sẽ xảy ra.

Tôi xin lỗi trước nếu bạn phát hiện thấy những sai sót rõ ràng trong mã bên dưới, vì tôi đang xây dựng lại các phiên bản mã bị từ chối từ trí nhớ của mình.

Mã xử lý ngắt trông như thế này:

ISR(INT0_vect) ( cli(); if (bitIsHigh(PINB, 0)) uart_send_char("1"); else uart_send_char("0"); sei(); )
Số 0 và số 1 chảy vào cổng, nhưng ngay lập tức rõ ràng là dữ liệu không chính xác - dữ liệu đó ít hơn nhiều so với dự kiến ​​và nó đã thay đổi khi cùng một yêu cầu được lặp lại. Trong quá trình tìm hiểu nguyên nhân, tất cả đều dẫn đến việc dữ liệu bị mất do truy cập vào giao diện uart, nhân tiện, giao diện này hoạt động ở tốc độ ổn định tối đa 38 kbit/s, trong khi I2C bản thân nó hoạt động ở tốc độ 100 kbit/s. Không thể tăng tốc độ của UART do thiếu tinh thể có tần số cần thiết để đưa UART đến tốc độ chấp nhận được. Vì vậy, cần phải loại bỏ công việc với uart khỏi phần ngắt. Có một cái gì đó như thế này:

Dữ liệu uint8_t tĩnh = 0; tĩnh uint8_t idx = 7; ISR(INT0_vect) ( cli(); dữ liệu |= bitIsHigh(PINB, 0)<< (idx--); if (!idx) { uart_send_char(data); data = 0; idx = 7; } sei(); }
Mọi thứ trở nên ổn định hơn nhưng dữ liệu vẫn không có ý nghĩa gì. Sau vài giờ làm việc thông qua thuật toán, bật xử lý STOP, v.v., người ta quyết định đi một con đường khác.

Đi đúng hướng

Cho dù tôi có cố gắng triển khai trình thám thính bằng mạch song song bao nhiêu đi chăng nữa thì cũng không có kết quả gì. Dựa trên điều này, chỉ còn một lựa chọn - cần phải đưa bộ vi điều khiển vào khoảng trống, tức là nó phải vừa là chủ cho thiết bị phản hồi vừa là nô lệ cho chủ ban đầu. Có lẽ nghe có vẻ khó hiểu nhưng thực tế không phải như vậy.

Vì atmega8 chỉ có một I2C phần cứng trên bo mạch nên rõ ràng là để hoạt động, bạn cần viết phần mềm hỗ trợ cho giao thức.

Kết quả là đoạn mã sau:

ISR (TWI_VECT) (CLI (); UINT8_T Trạng thái =TWSR; UINT8_T B; Char S; S = 0; _DLAY_MS (1); TÌNH TRẠNG & I2C_STATUS_MASK) (CASE I2C_STATUS_SR_RX_ADR_AC:/* CASE I2C_ST Atus_sr_rx_adr_nack:*/ uart_send_str ("- AW :"); uart_send_int(TWDR); i2csoft_start(); i2csoft_open_write(I2C_ADDRESS); break; trường hợp I2C_STATUS_SR_RX_DATA_ACK:/* trường hợp I2C_STATUS_SR_RX_DATA_NACK:*/ b = TWDR; sprintf(s, " %.2X", b); uart_send_str( s ); nghỉ byte (); uart_send_str(s); trường hợp TW_ST_DATA_ACK: b = i2csoft_read_byte(); uart_send_str("L\n"); phá vỡ;<Thiết bị chính được kết nối với I2C phần cứng của atmega, thiết bị thực thi được kết nối với 2 chân kỹ thuật số bất kỳ, một chân hoạt động ở chế độ SCL, chân còn lại ở chế độ SDA. Tất cả những gì đoạn mã trên thực hiện là nhận một ngắt qua I2C và gây ra trạng thái tương tự trên giao diện phần mềm, trong khi thông tin dịch vụ được ghi vào uart để giúp hiểu điều gì đang xảy ra. Độ trễ đã đặt đã được chọn cho một thiết bị cụ thể và có thể hơi khác đối với các thiết bị khác. Kết quả là chúng ta có được một máy đánh hơi rất tốt.

Nếu bất cứ ai quan tâm, các nguồn có thể được lấy từ