2020-11-23(“花式扫雷” 辅助制作)
地圖尋找
因為掃雷每次地雷位置都是隨機出現的,在IDA的導入表中找到rand函數,查一下交叉引用就可以發現只有sub_1003940這個函數中調用了它。再查sub_1003940的交叉引用可以發現只有sub_100367A函數調用了它。對sub_1003940下斷動態跟一下,就很容易可以發現,sub_1003940函數調用了rand函數生成了兩個隨機值,通過這兩個隨機值來決定在雷區地圖的哪個位置有地雷,很明顯,sub_100367A就是生成雷區地圖的函數。 然后我們就很容易能注意到兩個全局變量:dword_1005330(cnt_remainMine)存儲還剩下多少個地雷沒有初始化;byte_1005340(mine_map)存儲著雷區地圖
然后得到了掃雷地圖下一步就要找一找他的尋址邏輯了。這一步用CE也能觀察出來,但這里還是用IDA做一個分析,這樣能知其所以然。
首先我選了初級模式,地圖大小為9x9,我就在byte_1005340(mine_map)處下了一個大小為81字節的內存讀寫斷點,然后隨便選了一個坐標點進行動態,調了好一會最后在這里引起了我的注意
可以看到,這里push了edi和ebx,分別為3和6,這正是我選擇的坐標點!掃雷這種有明顯方格圖的程序,以傳入坐標點的方式進行判斷是非常合理的。然后,內存斷點所斷下的這個mov同樣可疑,它改變了雷區地圖的值,瞞猜這里al的0x41代表的就是[esi]這個位置已被翻開,那猜測這時[esi]就是雷區二維坐標 (6,3) 的線性表示。為了驗證這個猜想我們再從函數頭部找到esi的計算方式。
很明顯了,將縱坐標[edi]的值邏輯左移5位再加上橫坐標[edx]的值。前面雷區地圖初始化的時候rand了兩個值保存在eax和esi中,最后將mine_map[eax+esi]設置為地雷,而eax也做過 “shl ecx, 5” 這個操作。這證明了我們這個猜測是非常合理的,我們獲得了雷區地圖中地雷的保存算法。對比地圖可以發現:
但是我們跟入sub_1002646這個函數可以發現它只是調用了API對雷區的圖形界面進行一些改變,這就說明在這之前已經完成了對當前格是否是地雷的判斷,而且還沒有觸發我的內存斷點。仔細思考發現,坐標點(6,3)的線性表示是 0x66 = 102,而我的內存斷點的size設置的是81。那只需要重啟程序修改內存斷點size就可以了。果然,程序斷在了這里
通過對比mine_map計算出了此次游戲 (9,3) 坐標處是雷,選擇這個坐標調試可以看到在sub_100316B中,將 (9,3)這個位置的值由0x8F改成了0x80,但是繼續動調卻發現,這個位置雖然確實會做一個檢測,但是觸發斷點的并不是我們點擊鼠標后的操作,而是我們鼠標懸停時,程序訪問了我們鼠標懸停位置的雷區地圖,導致觸發了內存斷點。
這就麻煩了。我們知道,在掃雷游戲中,一旦點到了地雷就會使游戲終止,這說明當我們點到地雷的時候,必然有個地方檢測到了然后做了一個跳轉,跳過去結束了游戲,我希望能找到這個跳轉的地方,這樣就能劫持流程,實現 “即使我們點到地雷游戲也仍然會繼續” 的這樣的功能。但是因為鼠標的懸停也會訪問雷區地圖,使得我們不能通過內存斷點的方式找到跳轉前的那次判斷。 那這樣的情況怎么解決呢?這里我埋個伏筆,為什么會出現這樣的情況和如何解決我會在“call”這一節具體介紹
其實有了mine_map的地址,我們就已經可以實現外掛了。掃雷程序我們dump出他的雷區地圖不就已經無敵了嘛(手動滑稽)。用這個簡陋的idc腳本就可在IDA調試器附加的情況下輸出獲得當前這局游戲的雷區地圖。(注意要先將程序暫停再運行腳本)
from idc import * base = 0x1005340 for i in range(1,10):raw = []for j in range(1, 10):tmp = Byte(base+ (i<<5) + j)if tmp & 0x80 != 0:tmp = 1else:tmp = 0raw.append(tmp)print raw當時每次玩游戲都用調試器附加是不可能的,既然是外掛嘛,就得獨立。這里我用C++和python分別實現了讀取輸出地圖,原理上是一樣的,都是調用了kernel32.dll的OpenProcess和ReadProcessMemory API
#include <cstdio> #include <iostream> #include <windows.h> #include <winbase.h> #include <Tlhelp32.h> #include <stdint.h> using namespace std; #define MINEMAP_BASE 0x1005340 // 地圖基址 #define X_BASE 0x1005334 // 游戲棋盤寬 #define Y_BASE 0x1005338 // 游戲棋盤高 int X, Y;DWORD GetProcessIDByName(const char* pName) // 按進程名獲取PID {HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);if (INVALID_HANDLE_VALUE == hSnapshot) {return NULL;}PROCESSENTRY32 pe = { sizeof(pe) };for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {// char szExeFile[256];// sprintf(szExeFile, "%ws", pe.szExeFile); //如果是vs編譯器需要轉一下寬字節if (strcmp(pe.szExeFile, pName) == 0) {CloseHandle(hSnapshot);return pe.th32ProcessID;}//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);}CloseHandle(hSnapshot);return 0; }void print_map(BYTE * map) {BYTE tmp;for(int i=1;i<=Y;++i){for(int j=1;j<=X;++j){tmp = map[(i<<5)+j];if( (tmp & 0x80) != 0) //這里按位運算的優先級是低于不等于的tmp = 1;elsetmp = 0;printf("%d ",tmp);}printf("\n");} }int main(){DWORD PidGame = GetProcessIDByName("winmine.exe"); //獲取進程IDcout<<"ID:"<<PidGame<<endl;HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, PidGame); //獲取進程句柄BYTE mine_map[865];if(hProcess != NULL) {if (ReadProcessMemory(hProcess, (LPVOID)MINEMAP_BASE, mine_map, 0x360, NULL) == 1){cout <<"Read Success!"<< endl;ReadProcessMemory(hProcess, (LPVOID)X_BASE, &X, sizeof(X), NULL);ReadProcessMemory(hProcess, (LPVOID)Y_BASE, &Y, sizeof(Y), NULL);print_map(mine_map);}elsecout << "Read Fail!" << endl;}CloseHandle(hProcess);return 0; }也寫了個Py腳本來實現(感覺py調用這些API坑好多 ?_?
import os, psutil from subprocess import check_output from ctypes import * from ctypes.wintypes import * OpenProcess = windll.kernel32.OpenProcess ReadProcessMemory = windll.kernel32.ReadProcessMemory CloseHandle = windll.kernel32.CloseHandle PROCESS_ALL_ACCESS = 0x1F0FFF MINEMAP_BASE = 0x1005340 X = 30 # 棋盤X軸長度 Y = 24 # 棋盤Y軸長度def get_pid(name):# return map(int,check_output(["pidof",name]).split()) #Linuxreturn int( os.popen("tasklist /FI \"IMAGENAME eq %s\"" % name).read().split()[11] ) # 這種方法限制只能有一個同名進程try:pid = get_pid("winmine.exe")print("winmine.exe pid is:", pid) except IndexError as e:print("未找到進程!")exit(1) except ValueError as e:print("終端錯誤!")exit(1)address = MINEMAP_BASE # Likewise; for illustration I'll get the .exe header. buffer_ = c_char_p(("a"*0x360).encode()) # 設置緩沖區大小 bufferSize = len(buffer_.value) bytesRead = c_ulong(0) processHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)if ReadProcessMemory(processHandle, address, buffer_, bufferSize, byref(bytesRead)):print("Success:", buffer_) else:print("Failed.")raw_minemap = buffer_.value CloseHandle(processHandle)# print(buffer_.value) minemap = [] for i in range(1, Y+1):minemap.append(['{:<2}'.format(i)])for j in range(1, X+1):# print((i<<5) + j)tmp = raw_minemap[(i<<5) + j]if tmp & 0x80 != 0:tmp = '{:<2}'.format(1)else:tmp = '{:<2}'.format(0)minemap[i-1].append( tmp ) minemap.insert(0, ['{:<2}'.format(i) for i in range(X+1)]) for i in range(Y+1):print(minemap[i])# print(minemap[7][1]) # 快速查(7,1)處的值模擬點擊實現自動掃雷
拿到雷區地圖其實我們已經無敵了,但是呢,小編太懶了,高級模式甚至更大的自定義模式我對著地圖都懶得點_(:з」∠)_,那有沒有辦法可以自動的幫我點掉呢?當然可以,最容易想到的辦法就是計算出坐標實現一下鼠標的模擬點擊。模擬點擊的關鍵就是要測算一下坐標值,這里我用了Microsoft Spy++工具,因為消息數量太多,需要設置一下做個過濾:監視 -> 日志消息 -> “查找程序工具”選擇窗口 -> 消息 全部反選,勾選鼠標左鍵點擊的兩個消息事件WM_LBUTTONDOWN和WM_LBUTTONUP
然后我們對窗體做點擊操作就可以監聽到事件,看到相關的坐標值,計算一下每個格子的邊長就可以了
然后這里我發現一個問題,就是我用API發送一個坐標過去,但是發現點擊的位置不對,用Spy捕捉消息發現坐標和我參數填的坐標不一樣!這就很疑惑了,測試了發現應該是一種等比例縮放的關系,然后不同電腦測試還不一樣。具體原因我也不太確定,所以我代碼中的坐標是自己測試出來的,這個過程比較麻煩,就瞎猜。別的都是挺簡單的,就是調用PostMessage API,傳入坐標模擬對地圖上沒有雷的位置點擊即可,這里附上效果圖和部分代碼
call的尋找
我們還可以通過找call來快速的實現掃雷外掛。因為每次點擊,內部必然是調用了一個函數,往往會將我們點擊的坐標當作參數傳入點擊函數,然后用call指令調用這個函數,程序會在函數內做是否為雷等等的邏輯判斷和圖案繪制。那我們只要找到這個call的位置,手動注入shellcode,讓其調用這個call就可以實現自動的掃雷了。這種方法相較于模擬點擊更具有普適性,因為模擬點擊其實是受很多因素影響的,比如我這個坐標不一致的問題,個人感覺應該是窗口縮放的誤差。但是注入后進行call就不存在這樣的問題,因為程序固定,基址一定不變,基址不變,shellcode不變,調用的一定還是那個函數。 那接下來就要看一下怎么找這個call了。
通過前面的分析,應該可以知道 dword_1005118 和 dword_100511C 分別保存著這次點擊的X軸坐標和Y軸坐標,而byte_1005340則保存著雷區地圖。我們同樣先給雷區地圖下一個讀寫斷點,然后隨便點一個位置。正常情況下應該會在 0x010033EA 處斷下來(因為如果你第一次點到的位置就是地雷的話,程序會重置掃雷地圖,這樣你斷下來的位置可能不一樣,如果不一樣可以重試一下),而這個位置是隸屬于sub_10031D4的,可以在里面看到很多的判斷和調用分發,看一下這個函數的交叉引用可以看到確實在消息循環函數(sub_1001BC9)里,先做了兩次push,即傳了兩個參數后對它做了call調用,并且通過動態調試可以發現push的兩個參數分別就是我們點擊位置的X坐標和Y坐標!
好家伙,那是不是意味著已經找到這個call了呢?我直接打開代碼注入工具測試一下
有點問題,可以看到,我傳的參數是(1,1),注入后(1,1)處的格子確實是被翻開了,或者說它處于被按壓下去的狀態,但是沒有任何的反應,按道理格子被點擊后應該顯示四周有多少雷或者將四周都沒有雷的格子一并翻開,說明我們這樣做應該漏了什么。如果認真看了上一節的同學應該馬上就能猜到是什么原因了。鼠標點擊其實是兩種操作,分別是 WM_LBUTTONDOWN 和 WM_LBUTTONUP,在消息循環函數中接收到 WM_LBUTTONDOWN 消息,分發調用了 sub_10031D4 來實現對dword_1005118 和 dword_100511C這兩個全局變量賦值,并在窗口上繪制處按壓的圖案。那必然的,程序真正的邏輯在
WM_LBUTTONUP 消息所分發調用的函數里。 那怎么找那個函數呢,給地圖下內存斷點已經沒有用了,因為WM_LBUTTONDOWN就會將斷點觸發讓程序斷下來,WM_LBUTTONUP無法傳入。 我們可以通過在 0x1001BD2 處下條件斷點來實現
GetRegValue( “edx” ) == 0x202
當我們對窗口做任何點擊拖動最大最小化等操作時,其實本質都是將消息發送給窗口,這個消息是由Windows封裝好的一個結構化數據發送給程序的消息循環函數的,它的第二個參數Msg標志著消息的類型,比如我們點擊的是窗體的菜單時 Msg = WM_COMMAND;當我們左鍵按下時,Msg = WM_LBUTTONDOWN;當我們左鍵彈起時,Msg = WM_LBUTTONUP。 而 WM_COMMAND,WM_LBUTTONDOWN,WM_LBUTTONUP 這些其實都是Windows的宏,比如WM_LBUTTONUP就等于0x202,所以我這里下條件斷點的意思是當Msg為WM_LBUTTONUP時中斷程序。
我們這樣設置斷點再動調就可以看到程序調用了sub_10037E1,在這個函數里做的就是真正的判斷邏輯了。可能有的同學發現這個函數的調用并沒有傳任何的參數,因為它是通過dword_1005118 和 dword_100511C兩個全局變量來確定你點擊的位置的,而這兩個全局變量則是在sub_10031D4(WM_LBUTTONDOWN)時初始化好的。所以我們修改注入代碼,就可以實現掃雷了。
下面同樣先附上一張效果圖再加部分代碼,其中因為權限令牌和注入函數是模塊化封裝好的,我懶得改了,所以有不少代碼重復,比如句柄重開,在開發上這樣寫當然是不合適的,但是這兩個模塊化函數可以直接拿來用
DLL注入
前面講的方法是直接注入shellcode,這種方法在開發中往往不可取。因為每注入一段shellcode只能實現一個或者一次功能,要多次實現必然就要多次注入,也就是意味著要多次的申請和釋放空間。這個過程是比較危險的,容易出錯,所以在開發中往往采用的是直接注入DLL的方式,將整個輔助模塊注入進去。
要實現一個比較規范標準的DLL注入,就需要制作兩塊內容。一個是外置的輔助管理器,一個是要被注入到游戲程序里的輔助模塊。輔助管理器的功能就是復制輔助模塊的注入和卸載,而輔助模塊里實現的是真正的輔助功能。 這里先實現一下注入管理器
# define RETURN_FAIL(info) \ { \AfxMessageBox(info); \goto error; \ }CString GetAppPath() //返回應用程序的路徑 {char szPath[MAX_PATH];GetModuleFileName(NULL, szPath, MAX_PATH);CString strPath = szPath;strPath = strPath.Left(strPath.ReverseFind('\\')+1);return strPath; }bool InjectModder(HANDLE hProcess, CString strDll, bool bInject) {// 寫參數LPVOID pRemoteParam = ::VirtualAllocEx(hProcess, NULL, strDll.GetLength() + 1, MEM_COMMIT, PAGE_READWRITE);if (NULL == pRemoteParam) {::MessageBox(NULL, "遠程進程參數地址分配失敗", "失敗", MB_OK);return false;}DWORD dwWritten = 0;if (!::WriteProcessMemory(hProcess, pRemoteParam, strDll.GetBuffer(0), strDll.GetLength(), &dwWritten)) RETURN_FAIL(_T("向遠程進程空間寫入參數失敗"));// 檢查遠程輔助模塊是否已被注入PTHREAD_START_ROUTINE pRemoteFunc = (PTHREAD_START_ROUTINE)GetFuncAddr("kernel32.dll", "GetModuleHandleA");if (NULL == pRemoteFunc) RETURN_FAIL(_T("獲取GetModuleHandleA地址失敗"));HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, pRemoteFunc, pRemoteParam, NULL, NULL);if (NULL == hThread) RETURN_FAIL(_T("遠程線程啟動失敗"));WaitForSingleObject(hThread, INFINITE);DWORD dwModuleHandle;GetExitCodeThread(hThread, &dwModuleHandle); // dwValue為遠程進程輔助模塊句柄if (bInject) {if (dwModuleHandle) RETURN_FAIL(_T("輔助模塊已經注入,請勿重復注入"));PTHREAD_START_ROUTINE pRemoteFunc = (PTHREAD_START_ROUTINE)GetFuncAddr("kernel32.dll", "LoadLibraryA");if (NULL == pRemoteFunc) RETURN_FAIL(_T("獲取LoadLibrary地址失敗"));HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, pRemoteFunc, pRemoteParam, NULL, NULL);if (NULL == hThread) RETURN_FAIL(_T("遠程線程啟動失敗"));WaitForSingleObject(hThread, INFINITE);DWORD dwValue;GetExitCodeThread(hThread, &dwValue);}else {if (NULL == dwModuleHandle) RETURN_FAIL(_T("遠程輔助模塊未加載"));pRemoteFunc = (PTHREAD_START_ROUTINE)GetFuncAddr("kernel32.dll", "FreeLibrary");if (NULL == pRemoteFunc) RETURN_FAIL(_T("獲取FreeLibrary地址失敗"));hThread = CreateRemoteThread(hProcess, NULL, NULL, pRemoteFunc, (LPVOID)dwModuleHandle, NULL, NULL);if (NULL == hThread) RETURN_FAIL(_T("遠程線程啟動失敗"));WaitForSingleObject(hThread, INFINITE);DWORD dwValue;GetExitCodeThread(hThread, &dwValue); }::VirtualFreeEx(hProcess, pRemoteParam, strDll.GetLength() + 1, MEM_DECOMMIT);return true;error:::VirtualFreeEx(hProcess, pRemoteParam, strDll.GetLength() + 1, MEM_DECOMMIT);return false;}void CwinminecheatDlg::OnBnClickedStartmodder() // 注入輔助 {HWND hwnd = ::FindWindow(NULL, TEXT("掃雷")); //獲取游戲窗口句柄if (hwnd == NULL) {::MessageBox(NULL, TEXT("掃雷游戲未打開"), TEXT("錯誤"), MB_OK);return;}DWORD pid;GetWindowThreadProcessId(hwnd, &pid); // 通過窗口句柄得到進程IDHANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 通過進程ID得到進程句柄InjectModder(hProcess, GetAppPath() + MODDER_FILE_NAME, true); }void CwinminecheatDlg::OnBnClickedRemovemodder() // 卸載輔助 {HWND hwnd = ::FindWindow(NULL, TEXT("掃雷")); //獲取游戲窗口句柄if (hwnd == NULL) {::MessageBox(NULL, TEXT("掃雷游戲未打開"), TEXT("錯誤"), MB_OK);return;}DWORD pid;GetWindowThreadProcessId(hwnd, &pid); // 通過窗口句柄得到進程IDHANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); // 通過進程ID得到進程句柄InjectModder(hProcess, GetAppPath() + MODDER_FILE_NAME, false); }然后就是要實現一下輔助模塊了。輔助模塊內同樣也是采用調用call的方式來實現自動掃雷,這里與注入shellcode有些區別。因為注入shellcode時,我們的外掛程序與掃雷程序是兩個不同的程序,所以所需變量的讀寫都需要通過ReadProcessMemory WriteProcessMemory等API來實現。而現在,DLL直接存在于游戲的進程空間中,我們可以直接把基址寫進去轉成相應類型的指針就可以實現對所需變量的讀寫了。
// winmine_modder.cpp DWORD WINAPI ShowMainDlg(LPVOID pParam) { // 顯示輔助頁面// ::MessageBox(NULL, TEXT("輔助成功加載"), TEXT(""), NULL);ModderMainDlg dlg;dlg.DoModal();return 0; }BOOL CwinminemodderApp::InitInstance() // DLL在初始化時被調用 {CWinApp::InitInstance();m_hUIThread = ::CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ShowMainDlg, NULL, NULL, NULL);return TRUE; } BOOL CwinminemodderApp::ExitInstance() // DLL在釋放時被調用 {::TerminateThread(m_hUIThread, 0);::WaitForSingleObject(m_hUIThread, INFINITE);::CloseHandle(m_hUIThread);::MessageBox(NULL, TEXT("輔助卸載成功"), TEXT(""), NULL);return CWinApp::ExitInstance(); }記得要在MFC里創建一個頁面,再添加控件
// ModderMainDlg.cpp typedef struct _SweepMineParame {UINT u_x;UINT u_y; }SweepMineParame, * PSweepMineParame;DWORD __stdcall SweepMine(LPVOID lpParame) { // 這樣的寫法編譯器代碼優化一定要設置為最大優化PSweepMineParame pParame = (PSweepMineParame)lpParame;UINT u_x = pParame->u_x;UINT u_y = pParame->u_y;__asm {push u_ypush u_xmov eax, 0x10031D4call eaxmov eax, 0x10037E1call eax}return 0; }#define MINEMAP_BASE 0x1005340 // 地圖基址 #define X_BASE 0x1005334 // 游戲棋盤寬 #define Y_BASE 0x1005338 // 游戲棋盤高 #define GAME_STATUS 0x1005160 // 游戲狀態 0為正在游戲 2為掃雷失敗 3為掃雷成功 int X, Y;void ModderMainDlg::OnBnClickedSweepmine() {BYTE *mine_map = (BYTE*)MINEMAP_BASE;X = *(int*)X_BASE;// ReadProcessMemory(hProcess, (LPVOID)X_BASE, &X, sizeof(X), NULL); 等效于Y = *(int*)Y_BASE;// ReadProcessMemory(hProcess, (LPVOID)Y_BASE, &Y, sizeof(Y), NULL); 等效于for (int i = 1; i <= Y; ++i) {for (int j = 1; j <= X; ++j) {if (mine_map[(i << 5) + j] == 0) {SweepMineParame parame; // 獲取參數parame.u_x = j;parame.u_y = i;SweepMine(¶me);// Sleep(2);if (3 == *(int*)GAME_STATUS) { // 要判斷下當前游戲狀態 若該局游戲已結束仍繼續注入call會使游戲崩潰::MessageBox(NULL, _T("掃雷完畢"), _T("OK"), MB_OK);return;}}} }}
修改跳轉
除了模擬點擊、注入代碼,其實還有很多奇技淫巧,比如我們前面提到的,當點擊到地雷時,必然有一個判斷跳轉會去結束掉游戲,那如果我們找到了那個跳轉,將其跳轉流程改掉,那就算我們點到了地雷,也不會結束,我們就可以隨便點格子直到翻開全部格子。 要找這個位置需要一些耐心,多動調幾遍觀察函數作用。大體的方法就是下“WM_LBUTTONUP”斷點,輸出地圖對比非雷與雷點擊后代碼的運行路徑。這里我直接附上我的實現代碼
驅動讀寫
在最初寫掛的時候 BlackBinary神就建議我最好直接學習驅動,直接上驅動掛可以省去很多時間研究3環的對抗技術。掃雷雖然是不帶任何保護的一個小小小游戲,但也不影響我們用這款游戲展示驅動讀寫。 驅動的開發環境我用的是Windows 10 1809 + vs2019 + WDK 1809,這個版本對驅動的影響是比較大的,因為驅動會用到r0的API,低版本環境下的驅動能在高版本環境中運行,但高版本環境的驅動不一定能在低版本環境中運行。 還有一個注意點就是驅動編譯的架構一定要和調用驅動的程序編譯的架構相同。比如我驅動是用x64編譯的,那調用這個驅動的程序也一定要是x64架構的,不然指針上會有很嚴重的問題,基本就是藍屏(別問我為什么知道的這么清楚QwQ)
PS:這個Mdl讀寫驅動用的是這位大佬的項目,模塊化封裝的非常好,不會寫驅動的師傅看到也能用
Mdl讀取內存
驅動給出倉庫地址了,這里就不貼了,貼一下驅動的注冊和啟動代碼
sc create MyDriver binPath= "D:\DriverReadWriteProcess.sys" type= kernel start= demand sc start MyDriver pause sc stop MyDriver sc delete MyDriver如果提示驅動數字簽名無效不能啟動,可以進安全模式暫時關閉簽名保護或者用 64Signer 上個簽名
/* 讀取內存 */ #define Mdl_Read CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ALL_ACCESS)/* 寫入內存 */ #define Mdl_Write CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ALL_ACCESS)/* 連接符 */ const char* g_link = "\\??\\{F90B1129-715C-4F84-A069-FEE12E2AFB48}";/* 傳遞信息的結構 */ typedef struct _UserData {DWORD Pid; //要讀寫的進程IDDWORD64 Address; //要讀寫的地址DWORD Size; //讀寫長度PBYTE Data; //要讀寫的數據 }UserData, * PUserData;HANDLE hDriver = NULL;void read(DWORD pid, DWORD64 addr, LPVOID result, DWORD size) {UserData buf{ 0 };buf.Pid = pid;buf.Address = addr;buf.Data = (PBYTE)result;buf.Size = size;DWORD dwSize = 0;DeviceIoControl(hDriver, Mdl_Read, &buf, sizeof(buf), &buf, sizeof(buf), &dwSize, NULL);}int main() {hDriver = CreateFileA(g_link,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (hDriver == INVALID_HANDLE_VALUE){printf("[-] 驅動打開失敗 %d \n", GetLastError());return 0;}PidGame = GetProcessIDByName("winmine.exe"); //獲取進程IDcout << "PID:" << PidGame << endl;BYTE mine_map[865] = {0};read(PidGame, MINEMAP_BASE, mine_map, sizeof(mine_map));read(PidGame, X_BASE, &X, sizeof(X));read(PidGame, Y_BASE, &Y, sizeof(Y));print_map(mine_map);CloseHandle(hDriver);return 0; }總結
以上是生活随笔為你收集整理的2020-11-23(“花式扫雷” 辅助制作)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020-11-23(Windows系统
- 下一篇: 2020-11-24(dll注入的N种搞