【甜點】腳本外掛的檢測及預防
作者:amnesiac 首發:官方論壇之中文版
導言:一些遊戲在運行時能檢測並屏蔽腳本外掛,原理是什麼?有什麼辦法可以知道當前系統中開了多少腳本?包括使用默認托盤圖標、自定義圖標甚至隱藏了托盤圖標的腳本。還有,能發現已編譯成可執行文件的腳本嗎?是否能控制這些腳本??
檢測在運行的腳本
獲取系統中當前運行的所有 AutoHotkey 腳本的信息(下面的腳本獲取腳本標題和教程路徑):
DetectHiddenWindows, onWinGet, AHKWinList, List, ahk_class AutoHotkey Loop, %AHKWinList%{ AHKWinHWND := AHKWinList%A_Index% WinGetTitle, AHKWinTitle, ahk_id %AHKWinHWND% WinGet, AHKWinProcessPath, ProcessPath, ahk_id %AHKWinHWND% MsgBox, % "腳本標題:" AHKWinTitle "`n腳本進程路徑:" AHKWinProcessPath}return
由於可以獲取到窗口句柄,所以可以直接通過窗口命令獲取指定腳本的各種信息。其中腳本標題中包含了腳本文件名和執行腳本的 AutoHotkey.exe 版本號,例如:
D:SoftwareAutoHotkeyScripts est.ahk - AutoHotkey v1.1.15.00
當然,還可以使用其他方法。下面通過消息獲取某腳本的進程 ID:
AHKScriptName := "MyScript.ahk"SetTitleMatchMode, 2DetectHiddenWindows, onSendMessage, 0x44, 0x405, 0, , %AHKScriptName% ahk_class AutoHotkeyMsgBox %ErrorLevel% is the process id.
想判斷哪些是未編譯腳本, 哪些是已編譯腳本, 請看下圖:
圖中顯示當前系統運行了兩個 AutoHotkey 腳本,我們對比這兩個腳本的窗口名稱:
- D:SoftwareAutoHotkeyScripts est.ahk - AutoHotkey v1.1.15.00
- D:SoftwareAutoHotkeyScripts est.exe
可以看出一個是未編譯腳本,一個是已編譯腳本,因此可以根據窗口名判斷。註:從我使用這種方法開始到現在,窗口名稱的規律沒有發生變化,不過這裡無法保證以後的所有版本都會遵從。
對這些腳本進行控制
在寫腳本時,我們都知道可以很方便的對當前腳本進行控制,如:
Pause::Pause^!s::Suspend^!r::Reload^+q::ExitApp!l::ListLines!v::ListVars!k::KeyHistory
提示:我以前寫腳本時習慣加上許多這樣的熱鍵,包括暫停、重啟、列出熱鍵、列出執行行、列出變數、顯示按鍵歷史等,調試時很方便。那麼,對其他腳本我們可以實現這些功能嗎?
AHKScriptName := "MyScript.ahk"DetectHiddenWindows On ; 才可以檢測到腳本的隱藏主窗口.SetTitleMatchMode 2 ; 避免為下面的文件指定完整的路徑.WM_COMMAND := 0x111ID_FILE_PAUSE := 65403ID_FILE_SUSPEND := 65404PostMessage, WM_COMMAND, ID_FILE_PAUSE,,, %AHKScriptName% ahk_class AutoHotkeyPostMessage, WM_COMMAND, ID_FILE_SUSPEND,,, %AHKScriptName% ahk_class AutoHotkeyWinClose, %AHKScriptName% ahk_class AutoHotkey ; 關閉腳本,也可以使用消息,不過這裡使用窗口命令可能直觀一些。PostMessage, 0x111, 65305,,, %AHKScriptName% ahk_class AutoHotkeyPostMessage, 0x111, 65306,,, %AHKScriptName% ahk_class AutoHotkey
注意:當前我不清楚如何判斷腳本當前處於哪種狀態(當前腳本可使用 A_IsSuspended、A_IsPaused)。
還有哪些可用的消息?我提供幾點思路供參考:
- 執行操作時通過工具截取消息(消息很多,這些屬於 WM_COMMAND)
- 直接到官方論壇詢問作者
我比較懶,下面是通過源代碼提取的一些消息號(可能不完整,前面部分是消息號分配的說明):
0: unused (possibly special in some contexts)
1: IDOK2: IDCANCEL3 to 1002: GUI window control IDs (these IDs must be unique only within their parent, not across all GUI windows)1003 to 65299: User Defined Menu IDs65300 to 65399: Standard tray menu items.65400 to 65534: main menu items
消息號 | 含義
65300 | IDTRAYOPEN65400 | IDFILERELOADSCRIPT, IDTRAYRELOADSCRIPT65401 | IDFILEEDITSCRIPT, IDTRAYEDITSCRIPT
65402 | IDFILEWINDOWSPY, IDTRAYWINDOWSPY65403 | IDFILEPAUSE, IDTRAYPAUSE65404 | IDFILESUSPEND, IDTRAYSUSPEND65405 | IDFILEEXIT, IDTRAYEXIT65406 | IDVIEWLINES65407 | IDVIEWVARIABLES65408 | IDVIEWHOTKEYS65409 | IDVIEWKEYHISTORY65410 | IDVIEWREFRESH65411 | IDHELPUSERMANUAL, IDTRAYHELP
65412 | IDHELPWEBSITE註:一般而言在更新版本時消息號的用途不太可能發生變化,不過為了安全,在使用前最好明確其用途,否則可能發生意外情況。
實現的具體原理
在前面的腳本中我們應該注意到兩點:
- 開啟了對隱藏窗口的檢測
- 使用 AutoHotkey 類名獲取窗口
每個 AutoHotkey 運行時都有類名為 AutoHotkey 的主窗口(這個窗口為什麼稱為主窗口?請參閱 A_ScriptHwnd),與是否創建自定義 GUI 窗口(自定義窗口的類名為 AutoHotkeyGUI)、是否隱藏托盤圖標、是否打開調試窗口(這裡的調試窗口即是主窗口,未打開時為隱藏狀態)等無關。
說到這裡,我想起了一個有趣的事情:為什麼 AutoHotkey 被稱為模擬多線程呢?從這裡可以判斷出,每個腳本運行時至少有兩個線程:
- 主線程,運行主窗口,負責接受消息、緩衝熱鍵及偽線程的中斷和切換等這裡的描述可能不太準確和全面,不過大體上可以這麼理解,其中的偽線程是指幫助中所說的線程概念(例如 Thread 中所描述的線程,注意幫助中除了 A_ScriptHwnd 外從未涉及到主線程)。
- 腳本線程,實際執行腳本的線程,這不用多說了,它執行的就是我們寫的腳本。
從 Microsoft Spy++ 中可以看到,實際上每個腳本也只有兩個線程(你多開幾個熱鍵,多用幾個計時器並不會出現更多線程)。
小結
儘管未談到遊戲,不過主要內容前面都說完了。因此,要檢測 AutoHotkey 腳本的外掛只需在腳本啟動時及運行時定期執行:
DetectHiddenWindows, onWhile WinExist("ahk_class AutoHotkey") WinKill
遊戲不大可能用 AutoHotkey 寫,這裡的演示大家能看懂它的用途吧?不過這種檢測方法只是我猜測的,對於如 AutoIt、按鍵精靈這樣的腳本語言應該具有一定的可行性(如果是 C 語言這種估計沒那麼容易預防了)。正常的腳本如改鍵工具也無法例外了,有了解遊戲開發的朋友能否爆個真實情況。規避的方法也很簡單,自己下載源碼修改過類名編譯喔。
本文與遊戲關係不大,最開始的標題為腳本主窗口的妙用,不過這個標題是否更好呢?