微剋多資訊

 找回密碼
 註冊

Login

Login

搜索
回覆 1則 瀏覽 1190篇

[教學] Read / Write Process Memory

該用戶從未簽到

發表於 2012-11-11 23:48 | 顯示全部樓層 |閱讀模式
本帖最後由 edisonx 於 2012-11-16 17:33 編輯

壹、前言


Game Master 這款軟體相信不少人知道是幹嘛的,這裡只簡述及 AU3 實現。首先,MSDN 上幾個 API 需閱過,知道要幹嘛。
打星號的是關鍵,其它的基本上看一下就過去。這裡講的只是基本概念與原理,所以舉的例子較簡單、有設計過 (不然直接講後面也沒感覺)

(1) OpenProcess : 打開一個 process
(2) CloseHandle  : 關閉 process
(3) GetWindowThreadProcessId: 將取得之 HWND handle 轉成 Process ID
(4) *  ReadProcessMemory : 讀取特定 memory 數值
(5) *  WriteProcessMemory: 寫入特定 memory 數值

貮、建立被測試程式



這裡的被測試程式是由 C 語言寫的,附上執行檔 。
這是一個很無聊的猜數字遊戲,必須輸入 4 個數字 (答案是 123  456  789 347 ),
如果猜對的話會出現 You Got It !!
如果猜錯的話會一直出現提示:Try more...lease input 4 number :
執行結果如下參考。



重點是,執行結果上面會有四個數字,不同機器、每次執行都不保證一樣 (而且蠻可能不一樣的),
這四個數字代表「存答案的記憶體位置」,意思是,如果我們可以去讀得這四個記憶體的內容值,
我們就可以一次就猜出答案來。下面要講的就是這些東西。

參、 取得 Process  ID


大多的情況都是先取得視窗,再由視窗轉換成 Process ID。由視窗轉換成 Process ID 之 API 為 GetWindowThreadProcessId,這個 AU 裡面有幫我們包起來了,初步程式碼如下。

[code=AutoIt]
#include <WinAPI.au3>

Dim $hProcess
Dim $hWnd

Run("game.exe")
Sleep(1000)

$hWnd = WinGetHandle("[CLASS:ConsoleWindowClass]")
If @error Then ConsoleWrite("hWnd is NULL" & @CRLF)
_WinAPI_SetWindowText($hWnd, "game.exe")
$hProcess = _WinAPI_GetWindowThreadProcessId($hWnd, $iPID)
[/code]

肆、OpenProcess


取得 Process ID 後,再來是將 Process 打開進行後續處理。

[code=AutoIt]
#include <WinAPI.au3>
const $PROCESS_ALL_ACCESS = 0x1f0fff

Dim $hProcess
Dim $iPID, $iRead
Dim $hWnd
Dim $addr[4]
Dim $i, $ret

Run("game.exe")
Sleep(1000)

$hWnd = WinGetHandle("[CLASS:ConsoleWindowClass]")
If @error Then ConsoleWrite("hWnd is NULL" & @CRLF)
_WinAPI_SetWindowText($hWnd, "game.exe")
$hProcess = _WinAPI_GetWindowThreadProcessId($hWnd, $iPID)

$hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 0, $iPID)
if @error Then ConsoleWrite("_WinAPI_OpenProcess error" & @CRLF)
[/code]


伍、讀入記憶體,關閉 Process




接著是重頭戲了,因為一開始我們的受測程式 (game.exe) 有輸出四個  memory address,所以在我們的 AU 程式直接由 user 輸入記憶體位置,再由 ReadProcessMemory 去看裡面的內容。

[code=AutoIt]
#include <WinAPI.au3>
const $PROCESS_ALL_ACCESS = 0x1f0fff

Dim $hProcess
Dim $iPID, $iRead
Dim $hWnd
Dim $addr[4]
Dim $i, $ret

Run("game.exe")
Sleep(1000)

$hWnd = WinGetHandle("[CLASS:ConsoleWindowClass]")
If @error Then ConsoleWrite("hWnd is NULL" & @CRLF)
_WinAPI_SetWindowText($hWnd, "game.exe")
$hProcess = _WinAPI_GetWindowThreadProcessId($hWnd, $iPID)

$hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 0, $iPID)
if @error Then ConsoleWrite("_WinAPI_OpenProcess error" & @CRLF)

$sbuf = DllStructCreate("int")

For $i = 0 To 3
        $addr[$i] = Number(InputBox("ReadProcessMemory", "Input Address : ") )
        $ret = _WinAPI_ReadProcessMemory($hProcess, $addr[$i] , DllStructGetPtr($sbuf, 1), DllStructGetSize($sbuf),$iRead)
        $ret = DllStructGetData($sbuf, 1)
        ConsoleWrite($ret & @CRLF)
Next

_WinAPI_CloseHandle($hProcess)
[/code]

以我手邊,game.exe 最上面的四個 memory address 為 0022FF08 0022FF0C 00440EB0 00440EB4,
在 inputbox 依序輸入 0x0022FF08 0x0022FF0C 0x00440EB0 0x00440EB4 (16進位)

最後會就會顯示出結果出來。



上面的 123 456 789 347 就是結果了,直接拿這四組數值去 game.exe 輸入,一次就中。


陸、寫入記憶體



關於記憶體寫入,其實也是一樣的方式,因我們都已將問題簡化過,假設已知要寫入的位址了,這次要調用的 API 是 WriteProcessMemory,整個程式與上面幾乎是一樣,只有 WriteProcessMemory 不同而已。

這次的動作,我們將會把那四組記憶體內容,全改成 0 。意思是,改完之後,再回到 game.exe,輸入 4 個零就中了 (因原本的答案被我們改過了)

[code=AutoIt]
#include <WinAPI.au3>
const $PROCESS_ALL_ACCESS = 0x1f0fff

Dim $hProcess
Dim $iPID, $iRead, $iWrite
Dim $hWnd
Dim $addr[4]
Dim $i, $sVal, $ret

Run("game.exe")
Sleep(1000)

$hWnd = WinGetHandle("[CLASS:ConsoleWindowClass]")
If @error Then ConsoleWrite("hWnd is NULL" & @CRLF)
_WinAPI_SetWindowText($hWnd, "game.exe")
$hProcess = _WinAPI_GetWindowThreadProcessId($hWnd, $iPID)

$hProcess = _WinAPI_OpenProcess($PROCESS_ALL_ACCESS, 0, $iPID)
if @error Then ConsoleWrite("_WinAPI_OpenProcess error" & @CRLF)

; ---------------------------------------------------

$sbuf = DllStructCreate("int")
For $i = 0 To 3
        $addr[$i] = Number(InputBox("ReadProcessMemory", "Input Address : ") )
        ; ReadProcessMemory
        _WinAPI_ReadProcessMemory($hProcess, $addr[$i] , DllStructGetPtr($sbuf, 1), DllStructGetSize($sbuf),$iRead)
        $sVal = DllStructGetData($sbuf, 1)
        ConsoleWrite($sVal & @CRLF)

        ; WriteProcessMemory
        DllStructSetData($sbuf, 1, 0)
        _WinAPI_WriteProcessMemory($hProcess, $addr[$i], DllStructGetPtr($sbuf, 1), DllStructGetSize($sbuf), $iWrite)
Next

; ---------------------------------------------------
; WriteProcessMemory

_WinAPI_CloseHandle($hProcess)
[/code]

柒、其他


這次的示範就點到為止。感覺似乎沒什麼神奇的地方,但 Game Master 在單機處理上用的就是上述的兩大 API,除此之外還有以下動作

(1) 由於 Process 有尋訪權限問題,故必須再用多以下三個動作才是保險作法。

     a. OpenProcessToken 調整 process 權限
     b. LookupPriviledgeValue查詢 process 權限調整是否成功
     c. AdjustTokenPriviledges告知系統 process 權限已變更

(2) 這樣的程式.. 好像沒用耶?

     不可否認上述只是份簡單的 demo,最難的是做「搜尋」的部份。這部份很難做!Game Master 在搜尋穩定性也還不是最穩的,所以會有搜尋多次使得程式當掉的可能,同時搜尋結果只列其前 2500 還是前 10000 筆記憶體位置,多的就不會再往下找了。

     關鍵 API 是 VirtualQueryEx,這個 API 可以取得 Page 使用情況 (含起始位址與使用量,page 屬性),但詳細使用較為複雜,另一方面 AU3 並沒為這支 API 封裝成 UDFs,且需要有作業系統概念 ( 這點相信很多人比我強就是了) 故本文不予提及。

以上,本文敘述至此,希望能引起讀者對於 au 一些興趣。

by EdisonX

本帖子中包含更多資源

您需要 登入 才可以下載或查看,沒有帳號?註冊

x
您需要登入後才可以回帖 登入 | 註冊

本版積分規則

小黑屋|Archiver|微剋多資訊(MicroDuo)

GMT+8, 2016-12-3 19:33

Discuz! X

© 2009-2016 Microduo

快速回覆 返回頂部 返回列表