参考:
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 <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "mstcpip.h"
#include <string.h>
#include <shellapi.h>
#include <stdlib.h>
#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.%u\n", 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 ok\n");
//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:%d\n", HostLen);
for (i = 1; i < HostLen; i++)
{
if (host[i] < 0x20)
{
//DbgPrint("label%d\n", i);
host[i] = 0x2e;
}
}
// if (strstr((CHAR*)host + 1, "secret"))
if (IsLoggedHostHash(CalStringHash((char*)host + 1)) == 0)
{
printf("AnswerRRs:%u\n", AnswerRRs);
printf("host:%s\n", 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 %x\n", 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: %d\n", 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:%s\n", 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。
测试结果: