C++使用pacp得到exe文件的流量使用
【代码】C++使用pacp得到exe文件的流量使用。
·
#include <iostream>
#include <vector>
#include <unordered_map>
#include <thread>
#include <mutex>
#include <atomic>
#include <chrono>
#include <iomanip> // 用于格式化输出
#include <winsock2.h>
#include <iphlpapi.h>
#include <pcap.h>
#include <ws2tcpip.h>
#include <psapi.h>
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "Psapi.lib")
// 协议常量
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
// IP头结构
typedef struct ip_hdr {
unsigned char ip_verlen; // 版本(4) + 头长度(4)
unsigned char ip_tos; // 服务类型
unsigned short ip_len; // 总长度
unsigned short ip_id; // 标识
unsigned short ip_off; // 分片偏移
unsigned char ip_ttl; // 生存时间
unsigned char ip_p; // 协议
unsigned short ip_sum; // 校验和
struct in_addr ip_src; // 源IP
struct in_addr ip_dst; // 目的IP
} IP_HDR;
// TCP头结构
typedef struct tcp_hdr {
unsigned short th_sport; // 源端口
unsigned short th_dport; // 目的端口
unsigned int th_seq; // 序列号
unsigned int th_ack; // 确认号
unsigned char th_lenres; // 头长度和保留位
unsigned char th_flags; // 标志
unsigned short th_win; // 窗口
unsigned short th_sum; // 校验和
unsigned short th_urp; // 紧急指针
} TCP_HDR;
// UDP头结构
typedef struct udp_hdr {
unsigned short uh_sport; // 源端口
unsigned short uh_dport; // 目的端口
unsigned short uh_len; // 长度
unsigned short uh_sum; // 校验和
} UDP_HDR;
// 每个进程的流量统计
struct ProcessStats {
unsigned long long upload = 0;
unsigned long long download = 0;
bool is_start = false;
std::string name;
};
// 全局数据
std::unordered_map<DWORD, ProcessStats> g_processStats; // PID -> 统计
std::mutex g_statsMutex;
std::unordered_map<UINT64, DWORD> g_connectionMap; // 连接标识 -> PID
std::mutex g_connMutex;
std::vector<in_addr> g_localIPs; // 本机IP列表
std::atomic<unsigned long long> g_totalUpload{ 0 }; // 总上传字节
std::atomic<unsigned long long> g_totalDownload{ 0 }; // 总下载字节
// 函数声明
void GetLocalIPs();
void UpdateConnectionTable();
DWORD LookupProcess(const IP_HDR* ip, const void* transport, bool isTcp);
bool IsLocalIP(const in_addr& ip);
void PacketHandler(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* packet);
void DisplayThread();
// 生成连接键(64位,用于快速查找)
UINT64 MakeKey(DWORD localAddr, WORD localPort, DWORD remoteAddr, WORD remotePort, BYTE proto) {
UINT64 key = localAddr;
key = (key << 16) | localPort;
key = (key << 16) | remotePort;
key = (key << 8) | proto;
return key;
}
int main() {
// 初始化Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
std::cerr << "WSAStartup failed" << std::endl;
return 1;
}
// 获取本机IP地址
GetLocalIPs();
// 查找网络设备
pcap_if_t* alldevs;
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
std::cerr << "pcap_findalldevs: " << errbuf << std::endl;
WSACleanup();
return 1;
}
// 选择第一个可用设备(可根据需要改为用户选择)
pcap_if_t* device = alldevs;
if (!device) {
std::cerr << "No network devices found." << std::endl;
pcap_freealldevs(alldevs);
WSACleanup();
return 1;
}
// 打开设备
pcap_t* handle = pcap_open_live(device->name, 65536, 1, 1000, errbuf);
if (!handle) {
std::cerr << "pcap_open_live: " << errbuf << std::endl;
pcap_freealldevs(alldevs);
WSACleanup();
return 1;
}
// 编译过滤规则:只捕获IP数据包(IPv4)
struct bpf_program fp;
if (pcap_compile(handle, &fp, "ip", 0, PCAP_NETMASK_UNKNOWN) == -1) {
std::cerr << "pcap_compile: " << pcap_geterr(handle) << std::endl;
pcap_close(handle);
pcap_freealldevs(alldevs);
WSACleanup();
return 1;
}
if (pcap_setfilter(handle, &fp) == -1) {
std::cerr << "pcap_setfilter: " << pcap_geterr(handle) << std::endl;
pcap_freecode(&fp);
pcap_close(handle);
pcap_freealldevs(alldevs);
WSACleanup();
return 1;
}
pcap_freecode(&fp);
// 启动连接表更新线程(每秒更新一次)
std::thread connUpdater([]() {
while (true) {
UpdateConnectionTable();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
// 启动显示线程
std::thread display(DisplayThread);
std::cout << "开始捕获数据包,按 Ctrl+C 退出..." << std::endl;
// 进入捕获循环
pcap_loop(handle, 0, PacketHandler, nullptr);
// 清理(正常情况下不会执行到这里,因为pcap_loop是无限循环)
pcap_close(handle);
pcap_freealldevs(alldevs);
connUpdater.join();
display.join();
WSACleanup();
return 0;
}
// 获取本机所有IPv4地址
void GetLocalIPs() {
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == NO_ERROR) {
for (PIP_ADAPTER_INFO pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next) {
for (IP_ADDR_STRING* pAddr = &pAdapter->IpAddressList; pAddr; pAddr = pAddr->Next) {
in_addr addr;
if (inet_pton(AF_INET, pAddr->IpAddress.String, &addr) == 1) {
g_localIPs.push_back(addr);
}
}
}
}
free(pAdapterInfo);
}
// 判断IP是否为本机地址
bool IsLocalIP(const in_addr& ip) {
for (const auto& local : g_localIPs) {
if (local.s_addr == ip.s_addr) return true;
}
return false;
}
// 更新TCP/UDP连接表
void UpdateConnectionTable() {
std::lock_guard<std::mutex> lock(g_connMutex);
g_connectionMap.clear();
// 获取TCP连接表
PMIB_TCPTABLE_OWNER_PID pTcpTable = nullptr;
DWORD dwSize = 0;
DWORD dwRet = GetExtendedTcpTable(nullptr, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
if (dwRet == ERROR_INSUFFICIENT_BUFFER) {
pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
dwRet = GetExtendedTcpTable(pTcpTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
if (dwRet == NO_ERROR) {
for (DWORD i = 0; i < pTcpTable->dwNumEntries; ++i) {
auto& row = pTcpTable->table[i];
// 转换端口为主机字节序
WORD localPort = ntohs((WORD)row.dwLocalPort);
WORD remotePort = ntohs((WORD)row.dwRemotePort);
UINT64 key = MakeKey(row.dwLocalAddr, localPort, row.dwRemoteAddr, remotePort, IPPROTO_TCP);
g_connectionMap[key] = row.dwOwningPid;
}
}
free(pTcpTable);
}
// 获取UDP表
PMIB_UDPTABLE_OWNER_PID pUdpTable = nullptr;
dwSize = 0;
dwRet = GetExtendedUdpTable(nullptr, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0);
if (dwRet == ERROR_INSUFFICIENT_BUFFER) {
pUdpTable = (PMIB_UDPTABLE_OWNER_PID)malloc(dwSize);
dwRet = GetExtendedUdpTable(pUdpTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0);
if (dwRet == NO_ERROR) {
for (DWORD i = 0; i < pUdpTable->dwNumEntries; ++i) {
auto& row = pUdpTable->table[i];
WORD localPort = ntohs((WORD)row.dwLocalPort);
// UDP:本地地址+本地端口,远程填0
UINT64 key = MakeKey(row.dwLocalAddr, localPort, 0, 0, IPPROTO_UDP);
g_connectionMap[key] = row.dwOwningPid;
}
}
free(pUdpTable);
}
}
// 根据数据包查找进程ID
DWORD LookupProcess(const IP_HDR* ip, const void* transport, bool isTcp) {
std::lock_guard<std::mutex> lock(g_connMutex);
DWORD srcIP = ip->ip_src.s_addr;
DWORD dstIP = ip->ip_dst.s_addr;
WORD srcPort = 0, dstPort = 0;
BYTE proto = isTcp ? IPPROTO_TCP : IPPROTO_UDP;
if (isTcp) {
const TCP_HDR* tcp = (const TCP_HDR*)transport;
srcPort = ntohs(tcp->th_sport);
dstPort = ntohs(tcp->th_dport);
}
else {
const UDP_HDR* udp = (const UDP_HDR*)transport;
srcPort = ntohs(udp->uh_sport);
dstPort = ntohs(udp->uh_dport);
}
// 判断数据包方向
bool upload = IsLocalIP(ip->ip_src);
UINT64 key;
if (upload) {
// 上传:本地=源IP+源端口,远程=目的IP+目的端口
key = MakeKey(srcIP, srcPort, dstIP, dstPort, proto);
}
else {
// 下载:本地=目的IP+目的端口,远程=源IP+源端口
key = MakeKey(dstIP, dstPort, srcIP, srcPort, proto);
}
auto it = g_connectionMap.find(key);
if (it != g_connectionMap.end())
return it->second;
// 对于UDP,如果未找到,尝试仅用本地地址+端口查找(因为UDP表可能没有远程信息)
if (!isTcp) {
UINT64 localKey;
if (upload) {
localKey = MakeKey(srcIP, srcPort, 0, 0, proto);
}
else {
localKey = MakeKey(dstIP, dstPort, 0, 0, proto);
}
it = g_connectionMap.find(localKey);
if (it != g_connectionMap.end())
return it->second;
}
return 0; // 未找到,返回0(通常为系统内核或未知)
}
std::string GetExeFileNameByPid(DWORD pid)
{
// 1. 打开目标进程(需要足够的权限)
// PROCESS_QUERY_INFORMATION: 查询进程信息的权限
// PROCESS_VM_READ: 读取进程虚拟内存的权限
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProcess == NULL)
{
// 获取失败时可以打印错误码(可选)
// DWORD error = GetLastError();
// printf("OpenProcess failed, error code: %d\n", error);
return "驱动程序";
}
// 2. 定义缓冲区存储EXE路径
char szExePath[MAX_PATH] = { 0 };
std::string exeFileName;
// 3. 获取进程的EXE文件完整路径
if (GetModuleFileNameExA(hProcess, NULL, szExePath, MAX_PATH))
{
// 4. 从完整路径中提取文件名(如从 "C:\Windows\notepad.exe" 提取 "notepad.exe")
char* pFileName = strrchr(szExePath, '\\');
if (pFileName != NULL)
{
// 跳过反斜杠,取后面的文件名
exeFileName = pFileName + 1;
}
else
{
// 特殊情况:路径中无反斜杠,直接使用完整路径
exeFileName = szExePath;
}
}
// 5. 关闭进程句柄(必须释放资源)
CloseHandle(hProcess);
return exeFileName;
}
// 数据包回调函数
void PacketHandler(u_char* user, const struct pcap_pkthdr* pkthdr, const u_char* packet) {
// 假设以太网帧头(14字节)
const u_char* ip_data = packet + 14;
const IP_HDR* ip = (const IP_HDR*)ip_data;
// IP头长度
int ipHeaderLen = (ip->ip_verlen & 0x0F) * 4;
if (ipHeaderLen < 20) return;
// 只处理TCP和UDP
bool isTcp = (ip->ip_p == IPPROTO_TCP);
bool isUdp = (ip->ip_p == IPPROTO_UDP);
if (!isTcp && !isUdp) return;
// 数据包总长度(包括IP头)
unsigned short totalLen = ntohs(ip->ip_len);
// 确定方向
bool upload = IsLocalIP(ip->ip_src);
bool download = IsLocalIP(ip->ip_dst);
if (!upload && !download) return; // 非本机流量,忽略
const void* transport = ip_data + ipHeaderLen;
// 查找进程ID
DWORD pid = LookupProcess(ip, transport, isTcp);
// 更新统计
{
std::lock_guard<std::mutex> lock(g_statsMutex);
ProcessStats& stats = g_processStats[pid];
if (!stats.is_start)
{
stats.is_start = true;
stats.name = GetExeFileNameByPid(pid);
}
if (upload) {
stats.upload += totalLen;
g_totalUpload.fetch_add(totalLen, std::memory_order_relaxed);
}
else {
stats.download += totalLen;
g_totalDownload.fetch_add(totalLen, std::memory_order_relaxed);
}
}
}
// 显示线程:每秒打印每个进程的累计流量和实时速率
void DisplayThread() {
using namespace std::chrono;
auto lastTime = steady_clock::now();
unsigned long long lastTotalUpload = 0;
unsigned long long lastTotalDownload = 0;
std::unordered_map<DWORD, ProcessStats> prevStats;
while (true) {
std::this_thread::sleep_for(seconds(1));
auto now = steady_clock::now();
// 获取当前总累计
unsigned long long curTotalUpload = g_totalUpload.load(std::memory_order_relaxed);
unsigned long long curTotalDownload = g_totalDownload.load(std::memory_order_relaxed);
// 计算总速率
unsigned long long totalUpRate = curTotalUpload - lastTotalUpload;
unsigned long long totalDownRate = curTotalDownload - lastTotalDownload;
// 获取当前进程统计快照
std::unordered_map<DWORD, ProcessStats> curStats;
{
std::lock_guard<std::mutex> lock(g_statsMutex);
curStats = g_processStats; // 复制
}
// 清屏并输出
system("cls");
std::cout << "========== 网络流量统计(按进程) ==========" << std::endl;
std::cout << "总上传: " << curTotalUpload << " bytes (速率: " << totalUpRate << " B/s, "
<< totalUpRate / 1024.0 << " KB/s)" << std::endl;
std::cout << "总下载: " << curTotalDownload << " bytes (速率: " << totalDownRate << " B/s, "
<< totalDownRate / 1024.0 << " KB/s)" << std::endl;
std::cout << "--------------------------------------------" << std::endl;
std::cout << std::left << std::setw(8) << "PID" << std::setw(30)<<"名称"
<< std::setw(15) << "上传速率(B/s)"
<< std::setw(15) << "下载速率(B/s)"
<< std::setw(15) << "累计上传"
<< std::setw(15) << "累计下载" << std::endl;
for (const auto& entry : curStats) {
DWORD pid = entry.first;
const ProcessStats& cur = entry.second;
// 计算该进程的速率
unsigned long long upRate = 0, downRate = 0;
auto it = prevStats.find(pid);
if (it != prevStats.end()) {
upRate = cur.upload - it->second.upload;
downRate = cur.download - it->second.download;
}
std::cout << std::setw(8) << pid << std::setw(30)<< cur.name.c_str()
<< std::setw(15) << upRate
<< std::setw(15) << downRate
<< std::setw(15) << cur.upload
<< std::setw(15) << cur.download << std::endl;
}
// 更新上一秒数据
prevStats = std::move(curStats);
lastTotalUpload = curTotalUpload;
lastTotalDownload = curTotalDownload;
}
}
更多推荐



所有评论(0)