植物大戰殭屍,用QT注入程式碼,AT&T彙編語法

淡定君發表於2024-03-08

遇到了硬茬子,找了半天資料才找到,因為這個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的彙編,主要我是小白啥也看不懂,也沒有相對應的問題,真的我哭死,不過慢慢試可算是試出來了

最後的功能小彈框,很簡單:

相關文章