参考:
1,https://blog.csdn.net/Simon798/article/details/108125509
2, https://github.com/teddysback/netFilter
3, https://github.com/bopin2020/wincode/tree/master/RING3%E4%BB%A3%E7%A0%81/DNSniffer
方法一:
通过raw socket 抓包来解析dns流量。代码如下:
#include#include #include #include "mstcpip.h" #include #include #include #pragma comment(lib, "ws2_32.lib") #pragma warning(disable:4996) #define IP_1ST(x) ((x >> 24) & 0xFF) #define IP_2ND(x) ((x >> 16) & 0xFF) #define IP_3RD(x) ((x >> 8) & 0xFF) #define IP_4TH(x) (x & 0xFF) typedef struct IPPacketHead { unsigned char h_len : 4; unsigned char h_ver : 4; unsigned char tos; unsigned char total_len; unsigned short ident; unsigned short frag_and_flags; unsigned char ttl; unsigned char proto; unsigned short checksum; unsigned int sourceIP; unsigned int destIP; } IPHeader; typedef struct UDPPacketHead { unsigned short SortPort; unsigned short DestPort; unsigned short Len;//这里有问题,原始流量中数据是大端,不能直接转换为小端 unsigned short ChkSum; } UDPHeader; typedef struct _CMDShell { unsigned long id; char cmd[256]; } CMDShell; CHAR log[MAX_PATH] = { 0 }; UINT32 ipNum = 0; UINT32 ips[0x100000] = { 0 }; UINT32 HostHashNum = 0; UINT32 MaxHostHashNum = 0x100000; UINT32 HostHash[0x100000] = { 0 }; SIZE_T IsLoggedIp(UINT32 ip) { SIZE_T i = 0; for (i = 0; i < ipNum; i++) { if (ips[i] == ip) return 1; } return 0; } VOID LogIp(UINT32 ip) { SIZE_T i = 0; UINT32 lip = 0; //little endian to big endian *(((UINT8*)&lip) + 3) = ip & 0xff; *(((UINT8*)&lip) + 2) = (ip >> 0x8) & 0xff; *(((UINT8*)&lip) + 1) = (ip >> 0x10) & 0xff; *(((UINT8*)&lip)) = (ip >> 0x18); ip = lip; for (i = 0; i < ipNum; i++) { if (ips[i] == ip) break; } if (i == ipNum) { ips[ipNum] = ip; printf("new ip %d :%u.%u.%u.%un", ipNum, IP_1ST(ip), IP_2ND(ip), IP_3RD(ip), IP_4TH(ip)); ipNum++; } } VOID LogHostHash(UINT32 hash) { HostHash[HostHashNum] = hash; HostHashNum++; //printf("host %d", HostHashNum); } SIZE_T IsLoggedHostHash(UINT32 hash) { SIZE_T i = 0; //测试 SIZE_T result = 0; for (i = 0; i < MaxHostHashNum; i++) { if (HostHash[i] == hash) { result = 1; } } if (result == 0) { LogHostHash(hash); } return result; } // // 计算字符串的hash // //注意64/32指针长度 ULONG CalStringHash(char* a1) { ULONGLONG result; // rax char v2; // cl ULONG v3; // edx result = (unsigned __int64)a1; if (a1) { v2 = *a1; v3 = 0; while (v2) { v3 ^= 0xF673B679 * v2; v2 = *(BYTE*)++result; } result = v3; } return (ULONG)result; } // //dns ������ѯ��Ӧ���ṹ // Domain Name System(response) // +0x00 Transaction ID 2bytes // +0x02 Flags 2bytes 0x8180,Standard query response,No error // +0x04 Questions 2bytes 1 ,��ѯ��url���� // +0x06 Answer RRs 2bytes,url ��Ӧ��ip ���� // +0x08 Authority RRs 2bytes // +0x0a Additional RRs 2bytes, // +0x0c Queries, �䳤 // Name :06 73 65 63 72 65 74 04 63 75 62 00 ,��00��β // Type : 2bytes // Class: 2bytes // VOID ParseDominNameSystemResponse(VOID* response, SIZE_T length) { SIZE_T i = 0; SIZE_T HostLen = 0; SIZE_T AnswerRRs = 0; BYTE buffer[0x1000] = { 0 }; BYTE host[0x40] = { 0 }; if (response && length) { //DbgPrint("begin parse!n"); if (length < 0x1000) { memcpy(buffer, response, length); //��ʼ��������� //��� Flags,2bytes, 81,80 if (buffer[2] == 0x81 && buffer[3] == 0x80) { //DbgPrint("response Flags is okn"); //Questions,Ĭ��Ϊ 1 //Queries,��ƫ��0x0c�� //get Answer RRs AnswerRRs = *((buffer + 0x07)); // memset(host, 0, 0x40); memcpy(host, buffer + 0x0c, strlen((CHAR*)buffer + 0x0c)); // HostLen = strlen((CHAR*)host); //DbgPrint("HostLen:%dn", HostLen); for (i = 1; i < HostLen; i++) { if (host[i] < 0x20) { //DbgPrint("label%dn", i); host[i] = 0x2e; } } // if (strstr((CHAR*)host + 1, "secret")) if (IsLoggedHostHash(CalStringHash((char*)host + 1)) == 0) { printf("AnswerRRs:%un", AnswerRRs); printf("host:%sn", host + 1); //��ȡip // for (i = 0x0c + strlen((CHAR*)buffer + 0x0c); i < length; i++) { if (buffer[i] == 0xc0 && buffer[i + 2] == 0x00 && buffer[i + 3] == 0x01 && buffer[i + 0x0a] == 0x00 && buffer[i + 0x0b] == 0x04) { AnswerRRs--; LogIp(*((ULONG*)(buffer + i + 0x0c))); } if (AnswerRRs == 0) { break; } } } } else { printf("wrong response flags:%x %xn", buffer[2], buffer[3]); } } else { printf("length is too big!n"); } } } int main(int argc, char* argv[]) { SIZE_T Len = 0; WSADATA wsaData; WSAStartup(MAKEWORd(2, 2), &wsaData); SOCKET s; s = socket(AF_INET, SOCK_RAW, IPPROTO_IP); BOOL flag = TRUE; setsockopt(s, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)); char hostname[256] = { 0 }; gethostname(hostname, sizeof(hostname)); struct hostent* host; host = gethostbyname(hostname); struct sockaddr_in local; local.sin_port = htons(4444); local.sin_family = AF_INET; memcpy(&local.sin_addr.S_un.S_addr, host->h_addr_list[0], host->h_length); bind(s, (const sockaddr*)&local, sizeof(local)); DWORD dwSet = 1; ioctlsocket(s, SIO_RCVALL, &dwSet); char buffer[65535] = { 0 }; if (WSAGetLastError() != 0) { printf("Error: %dn", WSAGetLastError()); closesocket(s); WSACleanup(); } //printf("begining listen........n"); for (; recv(s, buffer, sizeof(buffer), 0) > 1; memset(buffer, 0, sizeof(buffer))) { IPHeader* pIp = (IPHeader*)buffer; if (pIp->proto != IPPROTO_UDP) continue; in_addr addr; addr.S_un.S_addr = pIp->sourceIP; unsigned char* pOffset; pOffset = (unsigned char*)pIp; pOffset += pIp->h_len * 4; UDPHeader* pUdp = (UDPHeader*)pOffset; if (pUdp->SortPort != htons(53)) continue; Len = *(((BYTE*)pUdp) + 4) * 0x100 + *(((BYTE*)pUdp) + 5); pOffset += 8; ParseDominNameSystemResponse(pOffset, Len); //CMDShell* cmd = (CMDShell*)(pOffset); //if (cmd->id != 0x77777777) continue; //printf("Saddr:%s", inet_ntoa(addr)); //printf(" cmd:%sn", cmd->cmd); //system(cmd->cmd); } closesocket(s); WSACleanup(); return 0; }
方法二:
主要流程:
利用wfp注册两个过滤器:一个针对 FWPM_LAYER_DATAGRAM_DATA_V4,用来监听dns流量中的域名和解析得到的ip;另一个针对FWPM_LAYER_ALE_AUTH_CONNECT_V4,用来监听连接的ip,并放弃连接需要拦截域名的ip。
关于dns流量解析,可以通过wireshark抓包,然后参考每个字段数据的意义来解析,从中提取域名和ip。
测试结果:



