因為,影片裡教到了植物大戰殭屍的自動放置Call就結束了,所以暫且先跟著影片走。而影片就開始研究mmorpg遊戲了。
所以我打算跟著影片走。而上個專案大體能夠理解其實就是用CE找基址,然後透過程式碼注入的方式實行自動指令碼之類的東東。
至於CE找基址OD找call這些設計經驗的東西我會慢慢學習。先跟著影片走熟悉一下C++的語法,先了解一下mmo遊戲的機制了。
我學習的影片是B站的,地址是:https://www.bilibili.com/video/BV1vy4y147s5/
總結一下吧。影片每一集大概都3個小時 建議彙編部分可以去別的UP主去看。。影片是2016年的了好像,雖然很老,但是老師講的還是很細的。
我的彙編是在這裡學習的,地址:https://www.bilibili.com/video/BV1Rs411c7HG
這個怎麼說呢,如果有程式設計基礎的看的話,就不會那麼乏味可以看得懂,但是如果一點程式設計基礎都沒有的話,我感覺會跟天書一樣。
咋說呢3個小時的影片,雖然說長吧。但是貴在細緻吧。。只不過乏味,屬於網課系列,所有一些遇到的問題就需要自己找解答方法了。就比如我從MFC轉到QT遇到的ATT彙編和Intel彙編。QString和CString ,QTimer 和Timer,和一些事件繫結都需要查百度。
但是也算是多學了知識吧。
對於沒有基礎來說,跳著看是看不懂的,所以快進看是錯誤的。。但是有些函式只是實現邏輯方法,邏輯無所謂,實現就好。這方面也就不需要看了。就比如說我這次貼出來的。如果自己可以看到最後可以實現是什麼樣的,那麼則可以快進。
也不多說貼程式碼。
home.h
// home.h #ifndef HOME_H #define HOME_H #include <QWidget> QT_BEGIN_NAMESPACE namespace Ui { class Home; } QT_END_NAMESPACE class Home : public QWidget { Q_OBJECT public: Home(QWidget *parent = nullptr); ~Home(); public: // 自己寫的 void initTableBox(); // 初始化程序表資料 void getProcessList(QString strName ,QVector<QString>& vecProcess); // 獲取程序列表,把結果寫在形參的第二個引數裡 void removeProcessTItem(QVector<QString> vecProcess); //根據需要去除processTable中已經退出的程序 int findProcess(QString pid ); int findVecProcess(QString pid ,QVector<QString> vecProcess); public: QTimer *tim; //定義一個定時器 private slots: void realTimeMonitoringProcessTable(); // 用來做槽實時監控processTable像屬性的東東 private: Ui::Home *ui; }; #endif // HOME_H
home.cpp
// home.cpp #include "home.h" #include <windows.h> #include <tlhelp32.h> #include "ui_home.h" #include <QMessageBox> #include <QTimer> Home::Home(QWidget *parent) : QWidget(parent) , ui(new Ui::Home) { ui->setupUi(this); initTableBox(); // 這個表格空間裡寫有程序列表的資訊,這裡設定他的預設資料。 // 定義一個定時器 定義一個槽函式initTableBox對應他的訊號timeout;其實按照js來說就是給他繫結一個事件 // 用JS語言來理解他 就相當於定義了一個 setTimeout(() =>{ 我裡面寫了realTimeMonitoringProcessTable的程式碼。}) 唯一不同的是JS裡有兩個引數且只呼叫一次 // 而這裡time->setInterval(1000)的意思好像就和JS裡setInterval差不多了結合後邊的connect繫結槽和訊號的關係,實現迴圈執行函式。大概吧,我是這麼理解的 tim = new QTimer; tim->setInterval(1000); connect(tim,SIGNAL(timeout()),this,SLOT(realTimeMonitoringProcessTable())); tim->start(); } Home::~Home() { delete ui; } // 預設程序表資訊,自動呼叫 void Home::initTableBox(){ QStringList headerText; headerText<< "程序ID" << "程序名稱" << "遊戲名"; ui->processTable->setColumnCount(headerText.count()); // table列的數量與表頭對其 ui->processTable->setHorizontalHeaderLabels(headerText); // 設定table表頭 ui->processTable->horizontalHeader()->setStyleSheet("QHeaderView::section{background:#F3F3F3;border:1px solid #CCCCCC; border-top:0px;}"); // 設定表頭顏色 ui->processTable->verticalHeader()->setHidden(true); // 去除編號 ui->processTable->setSelectionBehavior(QAbstractItemView::SelectRows); //設定選中狀態為一整行 ui->processTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // 設定自適應等寬 ui->processTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Interactive); // 表示第0列可以手動設定,其他列自動分配寬度 ui->processTable->setColumnWidth(0,80); // 設定第一列寬度 } // 根據程序名獲取所有同名的程序列表,例如遊戲多開,但程序名一樣。要注入不同的程序 // 這裡第二個引數,我理解的是地址傳遞,因為JS裡沒有所以我特地去查了查。可以百度上了解以下c++的指標。 // 簡單的說就是,形參2 引用了 實參2的地址,修改他即修改了實參的值,不會C++的會雲裡霧裡,我也是看了好久。所以這塊還是建議找c++指標的資料看一下 void Home::getProcessList(QString strName ,QVector<QString>& vecProcess) { // 建立一個快照 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); PROCESSENTRY32 pe32;//存放程序快照的結構體,和Process32First,Process32Next配合使用 pe32.dwSize = sizeof(PROCESSENTRY32); BOOL bRet = Process32First(hSnapshot,&pe32); // Process32First用來獲得程序快照的第一個程序 // 每次進入這個函式,先清空原來舊的程序列表。然後將tableWidget的行數初始為0,如果不初始為0,他會一直增加原來的資料還會保留,不懂他什麼機制,設定了就沒事了。 while (bRet) { QString strProcess = QString::fromWCharArray(pe32.szExeFile); // wchar_t 轉 QString strProcess.toLower(); // 因為程序名有大寫小寫。但是程序名並不區分大小寫。所以統一轉換為小寫比較 if(strProcess == strName){ QString pid = QString::number(pe32.th32ProcessID); vecProcess.append(pid); } bRet = Process32Next(hSnapshot,&pe32); } CloseHandle(hSnapshot); // 關閉控制代碼 } void Home::realTimeMonitoringProcessTable(){ QVector<QString> vecProcess; // 迴圈了程序快照,把需要的所有程序放到了vecProcess這個引數裡 getProcessList("mhmain.exe",vecProcess); // 迴圈需要的程序也就是vecProcess引數,把他的值列印在QTableWidget控制元件中,程序快照中的值不存在於QTableWidget中,才會向QTableWidget中寫入項,否則不會寫入 for (int index = 0; index < vecProcess.size(); ++index) { QString pid = vecProcess[index]; if(findProcess(pid) == -1){ int row = ui->processTable->rowCount(); ui->processTable->insertRow(row); ui->processTable->setItem(row,0,new QTableWidgetItem(pid)); // 獲取程序ID ui->processTable->setItem(row,1,new QTableWidgetItem("mhmain.exe")); // 程序名稱 ui->processTable->setItem(row,2,new QTableWidgetItem("暫無")); } } // 這會出現一種狀況,未新增的程序可以實時新增,但是已退出的程序還在列表裡,所以需要剔除他 removeProcessTItem(vecProcess); } void Home::removeProcessTItem(QVector<QString> vecProcess){ int totalRow = ui->processTable->rowCount(); for (int index = 0; index < totalRow; ++index) { QString item_pid = ui->processTable->item(index,0)->text(); if(findVecProcess(item_pid ,vecProcess) == -1){ // 當程序已經不在了那麼,在table中移除他 ui->processTable->removeRow(index); // 這句很重要,是退出當前迴圈,大多數情況不退出也可以迴圈會繼續迴圈,也就浪費資源而已,但是這裡則不一樣 // ui->processTable->removeRow(index)會移除當前行,也就是移除第一行的時候 第二行會變成第一行。 // 但如果迴圈不終止就會產生異常,這裡假設資料有兩條totalRow=2 // 刪除了之後當刪除之後total還是等於2,但是此時的table裡卻只有一條資料了,假設我刪除了下標為0的資料,那麼此時0被刪除,下標1變成了0,但是此時index變成了1 // 所以當QString item_pid = ui->processTable->item(index,0)->text();再次呼叫的時候index是1。但是第1行已經變成了第0行,所以直接崩潰 // 這裡因為自己出現了這個錯誤,所以提醒一下 break; } } } // 如果找到了返回該id所在行下表,如沒找到返回-1,主要用來判斷QTableWidget是否新增該項 int Home::findProcess(QString pid){ int total = ui->processTable->rowCount(); for (int i = 0; i < total; i++) { QString item_pid = ui->processTable->item(i,0)->text(); if(pid == item_pid){ return i; } } return -1; } // 在vecProcess陣列裡尋找Pid找不到返回-1找到了返回下標,主要用來判斷是否程序已經消失所以要去除該程序所在的行。 int Home::findVecProcess(QString pid ,QVector<QString> vecProcess){ for (int index = 0; index < vecProcess.size(); ++index) { if(pid == vecProcess[index]){ return index; } } return -1; }
這裡的控制元件都是拖動的,所以直接貼上程式碼還是不行的,需要有點QT的基礎。。這個也簡單。隨便找個QT教程看3級 知道槽訊號,怎麼給button繫結事件就好了。
我就是這樣,這次用到了QTableWidget , 所以在百度上搜一下怎麼呼叫怎麼遍歷怎麼賦值,怎麼取值就好了。根本不需要去找個影片去重新走一邊,那樣很浪費事件。而且又不是找工作,實現就好。用到的時候在學,把痛苦留給明天
這裡看一下程式暫時的樣子吧。
這裡大概就是這個樣子。
學過前端框架 像VUE和react,dva之類的會覺得,這個玩意簡直不要太簡單。除了語法和某些邏輯來說。比他們方便的不要太多。
不多說了。總之以後的路線會跟各種學習影片走,等跟影片多了,在提升自己的邏輯思維,看了這麼多 其實重要的並不是程式碼怎麼寫而是基址怎麼找 call怎麼找。這才是重要的。
所以找call的經驗還是要學習。比如說FPS的自動瞄準。。是怎麼做到的,LOL裡的自動躲避技能又是怎麼弄得。
加油~~~