遇到了硬茬子,找了半天資料才找到,因為這個QT是mingw編譯的,好像編譯器是gcc吧,我也不太懂,但是查了半天知道他的語法是AT&T,而我在學彙編的時候學的是8086,好像叫intel語法。所以開頭就碰壁到崩潰。。但是又不想放棄換MFC框架 。。也不想用QT5.0+的版本。因為畢竟以後還是高版本好用嗎。。不能碰到個岔子,就降低要求了是吧。。所以查了好幾天才解決了問題。AT&T語法教程太少了 ,基本上都是零零碎碎的一些回答,和一些其他的文章。
而且我遇到的問題並不能完全複製,因為我這個QT的編譯器是mingw 64位的,但是碰上的這個植物大戰殭屍是32位的。。所以遇到了很多困難。。在64位的編譯器上寫32位的程式碼還要考慮到相容問題。實在令我頭大,又不想放棄。因為一旦放棄意味著我要去學習MFC了。而且。我不可能以後做輔助碰到32位的用32位編譯器,注入64位的用64位編譯器吧。。那是不可能的,既然他們64位可以相容32位的,那他一定就可以找到解決辦法所以查了半天以自己的理解,才搞定了這幾行簡單的程式碼。雖然說就幾行程式碼,但是我查了很多天才組織出這幾行程式碼。
// 這是在MFC也就是,影片老師教的是這樣寫的,因為我學的也是intel語法所以,會彙編的都可以看的懂這些程式碼。我就不講解我的思路了 __declspec(naked) void putPlant(){ __asm { pushad mov ebx , [006A9EC0] mov ebx ,[ebx+768] push -1 push 2 mov eax,2 push 6 push ebx call 40D120 popad ret } }
而以下是AT&T語法,也是我找半天,才搗鼓出來的。
// 這些程式碼需要逐行分析 __declspec(naked) void putPlant() { // 還是那個原因因為用的是64位編譯器,所以pushad不可以用了。只能手動堆疊平衡 __asm volatile("push %rbx"); // -> pushad __asm volatile("push %rax"); // 語法不同,沒有需要講解的 __asm volatile("mov (0x6A9EC0),%ebx"); // -> mov ebx , [006A9EC0] // 尤其是這段程式碼。因為此編譯器是mingw64位的所以編譯器的暫存器是rbx,但是呢,我要注入軟體的這個軟體是32位的所以他的暫存器最大的就是ebx。 // 我當時試過這麼寫的 mov 0x768(%ebx) ,%ebx 但是編譯後的程式碼確是 // mov ebx, dword ptr ss:[bp+di+0x768] 和 add byte ptr ds:[eax], al // 他們甚至不在同一個記憶體單元裡。此時我的程式碼絕對會崩潰。在網上查了半天也沒有解決的方法。 // 最後我試了試如下程式碼。竟然可以了。。 // 其實按照邏輯來說的話 我真的不懂他是怎麼回事。因為在8086裡 他可能是這樣的 mov al ,[ ax + 768 ]; // 應該就是ax的低八位給了al 所以這裡就是 rbx的低八位給了%ebx // 因為需要注入的軟體是32位的所以他不可能超位元組,所以不需要擔心。 __asm volatile("mov 0x768(%rbx) ,%ebx"); // -> mov ebx ,[ebx+768] // START:以下沒有什麼可以需要講解的。百度上都可以查得到。 __asm volatile("push $-1"); // -> push -1 __asm volatile("push $2"); // -> push 2 __asm volatile("mov $2 ,%eax"); // -> mov eax,2 __asm volatile("push $6"); // -> push 6 // END // 這塊也一樣,push %ebx不好使,構建後直接報錯,但是push %rbx就可以。不知道為啥可能是相容後自動就變成ebx了把。 // 這裡報錯會出現 Error: operand type mismatch for `push' ,網上查了原因是:重定位檔案是32位,機器是64位,不相容出現的問題 __asm volatile("push %rbx"); // -> push ebx // 這塊出現的問題就很大了,我先開始寫的是 call $0x40D120 不行他會報:Error: unsupported syntax for `call',查都沒地方查去 // 因為我需要 0x40D120是個立即數,根據上邊的寫法call $0x40D120就應該是立即數但是不行 // 然後我去查了半天發現查出來的語法是這樣的 call *0x40D120 這時候編譯器總算是沒有報錯了。但是我執行的時候竟然報錯了。除錯後發現注入後的程式碼: // call *0x40D120 變成了 call dword ptr ds:[0x0040D120] 所以我程式直接崩潰了。。那麼反向思考。所以我最終的程式碼就變成了如下: __asm volatile("mov $0x40D120 ,%ebx"); // -> call 40D120 __asm volatile("call *%rbx"); // 堆疊平衡 __asm volatile("pop %rax"); // -> popad __asm volatile("pop %rbx"); // 這程式碼也很重要,是返回函式的。所有老師基本都有講 __asm volatile("ret"); }
正確注入的程式碼圖:
錯誤注入的程式碼圖:
完整的程式碼如下
#include "widget.h" #include "ui_widget.h" #include <windows.h> #include <QMessageBox> Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); } Widget::~Widget() { delete ui; } /* * 這中間穿插這上一節的程式碼,重複的就不新增了 */ __declspec(naked) void putPlant() { __asm volatile("push %rbx"); // -> pushad __asm volatile("push %rax"); __asm volatile("mov (0x6A9EC0),%ebx"); // -> mov ebx , [006A9EC0] __asm volatile("mov 0x768(%rbx) ,%ebx"); // -> mov ebx ,[ebx+768] __asm volatile("push $-1"); // -> push -1 __asm volatile("push $2"); // -> push 2 __asm volatile("mov $2 ,%eax"); // -> mov eax,2 __asm volatile("push $6"); // -> push 6 __asm volatile("push %rbx"); // -> push ebx __asm volatile("mov $0x40D120 ,%ebx"); // -> call 40D120 __asm volatile("call *%rbx"); __asm volatile("pop %rax"); // -> popad __asm volatile("pop %rbx"); __asm volatile("ret"); } // 秒殺的點選事件 void Widget::on_injectButton_clicked() { // 獲取程序ID DWORD pid = findGameProcessByWndTitle("植物大戰殭屍漢化版"); // 獲取程序控制代碼 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); // 在目標程序中申請虛擬記憶體 PVOID ThreadFunAdd = VirtualAllocEx(hProcess,NULL,4096,MEM_COMMIT,PAGE_EXECUTE_READWRITE); // 往指定記憶體中寫入程式碼 WriteProcessMemory(hProcess,ThreadFunAdd,LPCVOID(putPlant),4096,NULL); // 執行目標程序中的指定程式碼 CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunAdd,NULL,NULL,NULL); // 關閉控制代碼 CloseHandle(hProcess); }
後邊的點選事件不是我遇到的困難,這些照著API去看就好了,和老師教的沒啥區別。
主要是AT&T的彙編,主要我是小白啥也看不懂,也沒有相對應的問題,真的我哭死,不過慢慢試可算是試出來了
最後的功能小彈框,很簡單: