連連看輔助


轉載自大神CSDN博主「九陽道人」

版權聲明:本文為CSDN博主「九陽道人」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。 原文鏈接:https://blog.csdn.net/qq_31507523/article/details/88309060

QQ連連看單機版輔助制作全流程

最近在15PB學習逆向,分析了個小游戲並寫出了輔助工具,在這里總結下全流程。

游戲:QQ連連看(單機版1.2)

完成目標:

1.去除廣告

2.完成指南針、炸彈消除的功能

3.編寫注入程序和游戲輔助的DLL

使用工具:VS2017、OD、CE、PEID、Spy++

分析環境:Win7虛擬機

首先在百度上搜索“QQ連連看單機版”隨便下了個V1.2版的,把它解壓到桌面然后進入文件夾。

仔細觀察下都有些什么文件,這很重要,有可能得到一些有利於破解的信息。

1.在這里觀察到有兩個可執行的EXE文件、兩個音樂文件夾,這都是比較重要的信息。

1565407736276

2.用PEID擦看下這兩個EXE程序基本信息,得以得出"kyodai.exe"程序是VC6.0的編譯器編寫的,游戲一般都是C++語言編寫,而"QQ連連看單機版V1.2.exe"是一個加殼的程序,常見壓縮殼ASpack這就忽略它,也能分析。

1565407757730

1565407773830

3.雙擊"kyodai.exe"程序后回崩潰如圖。

1565407789849

4.那么就確定了開始游戲的程序"QQ連連看單機版V1.2.exe",打開之后會彈出廣告窗口,再之后游戲就運行了。

1565416222982

5.退出游戲,打開”任務管理器”,在運行游戲觀察進程的變化,發現在彈出廣告窗口時只有一個與游戲相關的進程”QQ連連看單機版V1.2.exe”

1565416238046

6.點擊繼續進入游戲之后發現此進程消失,而多了另一個很熟悉進程"kyodai.exe"

1565416250432

7.由於最終運行的還是"kyodai.exe"程序,但是點擊它又運行不了。而通過其他程序卻可運行它, 由此猜想"kyodai.exe"在運行時,”QQ連連看單機版V1.2.exe”打開它並對它的內存進行了修改,使他能正常運行。在這說明下這是老編譯器寫的游戲不存在隨機基址,很多地址都是固定的。

猜想它可能使用了CreateProcessA\W函數打開進程,那么開始調試,使用OD開打”QQ連連看單機版V1.2.exe”,Ctrl+G搜索CreateProcessA\W在2個函數上都按F2設置斷點,運行之后發現在CreateProcessA函數上斷下,觀察堆棧數據,發現"kyodai.exe"程序被它以掛起的方式打開了。

1565416281645

8.很多惡意程序都是這樣的套路,那么就容易猜想了,掛起了程序之后就可能修改內存數據,之后恢復程序,那再就來搜索一下修改內存數據函數WriteProcessMemory下斷點,運行,在次停下了再觀察堆棧數據,發現3個重要的數據,意為在"kyodai.exe"程序中的0043817A 地址處修改1個字節的數據。

1565416294454

9.選中該Buffer右鍵查看其內容是”0”。

1565416304273

10.然后在搜索一個喚醒線程的函數ResumeThread下斷點,運行之后果然走到了這里。

1565416315593

11.那么我們現在就已經分析出了”QQ連連看單機版V1.2.exe”的運行機制,它首先彈出廣告,然后再執行CreateProcessA -> WriteProcessMemory -> ResumeThread操作打開"kyodai.exe"程序,那么我們現在就可以模擬它的執行流程編寫程序打開它

打開VS2017編寫程序如下

#include "pch.h"
#include <iostream>
#include <windows.h>

//kyodai.exe程序的路徑
#define CATALOG L"C:\\Users\\15pb-win7\\Desktop\\連連看\\kyodai.exe"
int main()
{
STARTUPINFO si = {};
//操作進程的信息結構體
PROCESS_INFORMATION pi = {};
//打開進程
CreateProcess((LPWSTR)CATALOG, 0, 0, 0, FALSE,
CREATE_SUSPENDED,//此參數為掛起主線程
0, 0, &si, &pi);
DWORD dwWrite = 0;
//修改1字節的數據
WriteProcessMemory(pi.hProcess, (LPVOID)0x43817A, "\x0", 1, &dwWrite);
//恢復線程
ResumeThread(pi.hThread);
return 0;
}

12.本人認為這段程序對咱們新手來說比較有用。在這里要注意的是”kyodai.exe”所在路徑每個人的可能都不一樣,編譯程序后運行,打開游戲成功!

1565416380190

13.當然也可以直接使用16進制文件編輯器直接找到3817A(因為默認加載基址0x400000需要減去)地址把里面的數據改為“00”,我使用的工具是010Editor。

1565416391301

14.接下來正式分析游戲關鍵功能了,正常的程序匯編中CALL xxx基本都是函數,運行游戲之后多玩幾把測試一下游戲玩法,游戲右上角道具欄有指南針,多刷新幾次地圖可能會出現炸彈,把炸彈消除掉道具欄里會出現炸彈,而且道具有數量限制。

1565416403574

15.我用了CE工具查找道具基址,很遺憾找了10多分鍾沒找到,只有換一種方法了,點擊道具的時候會發出聲音,那么音樂文件的名字是字符串,搜索字符串可以是個切入點。

1565416412285

16.還有點擊練習的時候地圖會隨機刷新,那么肯定會用到rand這個隨機函數,親測兩種方法都能達到一樣的效果找到關鍵點,在這里總結下查找API的方法,使用OD打開或者附加”kyodai.exe”程序先運行起來,再在OD中Ctrl+G搜索rand函數下斷點,點擊游戲中的練習后斷了下來。

1565416426499

17.然后點擊OD菜單欄上的”K”進行棧回溯分析。

1565416434699

觀察發現有2個上層調用(有程序的名字的2個),他們的關系是0x41A080處的函數調用0x41CAF2的函數,0x41CAF2的函數調用rand。這里說的比較啰嗦,反正在”K”中越靠下就越是外層的函數。

1565416445825

18.雙擊進入第一個地址並在上面設置斷點,然后再點K雙擊進入第2個地址設置斷點然后把之前rand的斷點刪除

1565416463248

1565416472669

19.F9運行后再次在游戲中點擊練習會在第一個斷點0x41A080地址上停下這里是一個函數(CALL),要重點關注一下它上面行代碼MOV ECX, EDI 由於這是C++所寫的程序,它都會使用ECX這個寄存器傳遞this指針也就是傳遞一個對象,之后看見只要對ECX寄存器操作的代碼都要留意一下。按F7進入這個函數,先Ctrl+A讓OD幫我們分析一下該模塊,發現一開始把ECX的值給了ESI,先不管這繼續單步然后沒幾步就看見了熟悉的字符串”strat.wav”(字符串搜索也能定位到這個函數里來),這已經證明這個函數就是初始化游戲的函數。

1565416483525

20.然后再快速單步很快就能發現剛才所下的第二個斷點,發現rand下面有一個 memcpy復制內存的函數,先大致走一遍注意觀察OD中右上角寄存器的變化,觀察寄存器上的值的內存的變化,走完了好像什么也沒發現…然后再重新運行再多單步跟蹤幾次這個函數總會發現一些什么的。

1565416494177

21.再次跟蹤這個函數,實際上剛才說過要注意ECX的值的傳遞,在這個函數里ECX傳遞給了ESI,那么我們要對所有操作ESI的代碼留意一下,在加上我們下的第二個斷點0x41CAF2所在附近,我們就要對它附近ESI重點關注了,在0041CAFC 地址處的這一行代碼對ESI進行了訪問,走一步之后我們發現EAX的是0012BB50。

1565416504710

22.那么再選中它右鍵->數據窗口跟隨 查看0012BB50地址里面有些什么數據。

1565416514028

23.然后走到0041CB10 地址處,這里會調用memcpy復制內存,走一步觀察0012BB50地址里的數據除了第一個DC 00…沒變,后面都被填充成某種規律的010101…

1565416524666

24.在繼續走兩步經過0041CB20 地址出的函數后驚訝的發現0012BB50地址里那一大串數據又被刷新,到此我們大膽猜測0012BB50這就是游戲的地圖數組基地址。

1565416536898

25.為了驗證是否正確按F9把游戲運行起來觀察地圖與該內存數據的是否有聯系(觀察地圖和該內存時刷新出一個特點鮮明的地圖較好,比如地圖右上角有兩個靠的近相同的東西等)我多刷新了幾次得到一個左上角和右下角都被填滿了的地圖,觀察OD中的內存(記得點擊下OD數據才會刷新)也跟着改變了,貌似還找到了數組的邊界!0012BB58(第一個點)

1565416544743

26.再刷新幾次地圖找到第一行有兩個連着的相同的東西,發現地圖和OD中的內存非常相似。

1565416553410

27.為了確認 我們把游戲中的物品點擊消除掉在觀察內存發現果然被清0了那么可以確定0012BB50這就是地圖數組基地址了,並且我們得到了0012BB58就是地圖數據的起始位置。

1565416563802

28.接下來分析指南針和炸彈,指南針功能是可以幫助玩家找到2個相同物品,炸彈是找到2個相同的物品並消除,這2個道具不管是誰都會遍歷地圖數組,才能實現他們的功能,所以在0012BB58地址處右鍵下一個內存訪問斷點。

1565416574502

29.先在OD中按ALT+B把其他斷點禁止或者刪掉,然后運行游戲點擊指南針,程序會停下來。點擊”K”進行棧回溯分析觀察他的上幾層調用函數發現有5個那么每個都點進去設置上斷點,然后右鍵選中先把0012BB58的內存斷點給刪除掉。

1565416584279

30.再F9運行OD再運行幾次發現都停在004292A5 地址上那么這肯定是不需要的函數把它斷點去掉,再運行2次,點不動了需要點擊游戲界面,一點就在OD中斷下來了 因此判斷0040CACA斷點也不是我們想要的,同樣的繼續運行幾次還是都停在了0041AF11 處,這也是無用地址去掉斷點,再F9游戲就運行起來了,這個過程多調試幾次就會明白的。

因此0041DE5C和0041E76C 處的函數就可能是我們能利用關鍵函數,現在想要寫代碼完成指南針的功能,只要找到這兩處地址的函數調用時所需要的參數,那就可以模擬出指南針的功能。

重新運行起來,點擊游戲中的指南針,會在0041DE5C 處的代碼斷下,選中這一行按Enter建進入這個函數一直往下拉找到末尾RETN觀察返回值發現RETN 0xC 也就是說0041DE5C 地址處的函數調用需要3個參數,在觀察堆棧得出參數是 0,0,F0。

1565416599545

31.為了確認這3個參數是不是可變的,再次運行游戲,再點擊指南針,再次在0041DE5C 停下發現堆棧里的參數還是這么多沒有改變,那我就決定調用了這個函數模擬指南針功能了,寫好注釋。 其實在第2個斷點0041E76C 處的參數更少,只有2個,但是在那里斷下來之后查看堆棧里的參數也不知道是些什么,所以放棄它把它的斷點去掉。

相同的原理我再找到炸彈的調用地址,多刷新幾次游戲,找到有可以消除炸彈地圖,把炸彈消除了道具欄中就出現了炸彈。

1565416611362

32.同樣運行游戲之后在0012BB58下內存訪問斷點,再點擊游戲道具欄里的炸彈,斷下之后再次按”K”進行棧回溯分析,發現這幾個地址很眼熟,和之前找指南針的棧回溯基本一致 只有一個不一樣,那么無用幾個地址不用管他,我們在0041DE5C 處下斷點,刷新游戲地圖找到一個有炸彈的地圖,再使用炸彈之后停下觀察堆棧參數的變化,發現參數是0,0,F4。

1565416620756

由此我們可以對比下兩次調用這個函數的參數

調用指南針是0,0,F0

調用炸彈是0,0,F4

得出結論第3個參數就是游戲的道具類型,0041DE5C 地址處的函數功能應該就是使用道具。

33.談一下我對輔助中的的DLL,注入程序,原程序(這里就是游戲程序)他們關系的理解:我們想要DLL里面的功能在原程序種實現,直接來是不行的,需要通過注入程序把DLL注入到原程序中,這時DLL功能才會對原程序產生影響。 這就像熱發燒了打針一樣,針筒里的葯水就是DLL,針筒就是注入程序,人體就是原程序。

34.接下里就是編寫輔助了,在這需要實現指南針和炸彈的功能,之需要模擬調用0041DE5C 地址處的函數就OK了。

我用的是VS2017首先創建一個MFC的DLL程序,選擇在靜態庫中使用MFC,這個選項也可以在屬性->常規里面更改。

1565416634318

35.使用到的關鍵API如下

FindWindow獲取窗口句柄

SetWindowLong設置窗口回調

_beginthreadex創建線程便於彈窗

因為要在游戲中彈出一個窗口,所以要添加一個資源Dialog

窗口回掉原型為

LRESULT

CALLBACK

DefWindowProc(

In HWND hWnd,

In UINT Msg,

In WPARAM wParam,

In LPARAM lParam);

要實現的輔助功能主要就是在這個窗口回掉函數里面實現。

36.現在先使用VS中的自帶工具Spy++先查找游戲的,窗口名,和類名,用於的FindWindow函數獲取窗口句柄。

把Spy++中的望遠鏡拖到游戲窗口上可以可到,窗口名。

1565416650983

1565416665152

37.再次分析下0041DE5C 地址的函數這里的參數是3個但之前也說過C++使用ECX傳遞對象,所以我們必須得到ECX的值,經過多次運行發現右上角的ECX值一直是0012A688所以我們直接使用這個固定值。

1565416676992

38.使用_asm內嵌匯編調用那個關鍵函數,0041DE5C 地址處匯編是CALL [EAX+0x28] ,在內存窗口中Ctrl+G搜索EAX+0x28 里面的值為0041E691(以小端方式讀) 這就是需要調用函數的地址。

LRESULT
CALLBACK
MyDefWindowProc(
_In_ HWND hWnd,
_In_ UINT Msg,
_In_ WPARAM wParam,
_In_ LPARAM lParam)
{
//指南針消息
if (Msg == WM_SIGN1)
{
_asm
{
MOV ECX, 0X12A688;    //this指針
PUSH 0XF0;            //參數3是道具類型
PUSH 0;               //參數2
PUSH 0;               //參數1
MOV EAX, 0X0041E691;
CALL EAX;             //指南針函數
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
if (Msg == WM_SIGN2)
{
_asm
{
MOV ECX, 0X12A688;
PUSH 0XF4;
PUSH 0;
PUSH 0;
MOV EAX, 0X0041E691;
CALL EAX;//炸彈函數
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return CallWindowProc(g_OldProc, hWnd, Msg, wParam, lParam);
}

39.說明一下,為了在輔助程序的窗口發送消息,我在頭文件stdafx.h中自定義了兩個消息,然后編譯生成DLL就OK。

1565416728499

1565416737795

1565416746284

40.最后編寫遠程注入程序,就創建一個控制台程序就行了,要注意的是在這里要在屬性里選擇C+±>代碼生成把運行庫改為多線程調試(\MTd) 就跟上面的DLL靜態編譯一樣,如果不這樣,我們寫的程序在別人的電腦上可能就運行不了。

1565416757512

41.遠程線程注入的代碼基本都固定,死記硬背就行了,在此貼上。

#include "pch.h"
#include <windows.h>
#include <string.h>

//注入DLL函數
void InjectDll(HWND hWnd, const char* DllPath)
{
//獲取要注入的進程的PID並打開它
DWORD dwPid = 0;
GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess)
{
printf("打開進程失敗\n");
getchar();
return;
}
//在注入的進程中分配一塊虛擬內存
LPVOID lpAddr = VirtualAllocEx(hProcess,
NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (!lpAddr)
{
printf("分配內存失敗\n");
CloseHandle(hProcess);
getchar();
return;
}

//把dll路徑寫入到目標進程空間中
DWORD dwWrite = 0;
WriteProcessMemory(hProcess, lpAddr, DllPath,
strlen(DllPath) + 1, &dwWrite);
if (strlen(DllPath) + 1 != dwWrite)
{
printf("dll路徑寫入失敗\n");
VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
getchar();
return;
}

//創建遠程線程
HANDLE hThread = CreateRemoteThread(hProcess, 0, 0,
(LPTHREAD_START_ROUTINE)LoadLibraryA,
lpAddr, 0, 0);
if (!hThread)
{
printf("遠程線程創建失敗\n");
VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
CloseHandle(hProcess);
getchar();
return;
}

//等待線程結束(無窮大毫秒)
WaitForSingleObject(hThread, INFINITE);

//釋放
VirtualFreeEx(hProcess, lpAddr, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
}

//動態庫DLL的名字
#define DLLNAME "\\01連連看輔助.dll"

int main()
{
//dll的全路徑名
char dllNamePath[256] = {};
//獲取DLL的全路徑
GetCurrentDirectoryA(sizeof(dllNamePath), dllNamePath);
strcat_s(dllNamePath, DLLNAME);
//獲取游戲窗口句柄
HWND Handle = FindWindow(NULL, L"QQ連連看");
//調用遠程注入函數
InjectDll(Handle, dllNamePath);
return 0;
}

42.這里說下游戲輔助常用的API函數GetCurrentDirectoryA這個API可以獲取到本程序的當前目錄的字符串,只要把要DLL放在與注入程序同一個目錄了,直接運行注入程序就注入DLL了,這個非常方便。

1565416812292

43.把DLL文件和注入程序生成放在同一個目錄下,先打開游戲,然后點擊注入程序,單機版連連看的輔助就順利完成了,運行秒殺效果如圖。

1565416820759

最后感謝15PB的栽培

游戲和源碼網盤鏈接: 鏈接:https://pan.baidu.com/s/1LyAn27Y__beBpCewMIr-WA 提取碼:oq08


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM