標籤:

【甜點】腳本外掛的檢測及預防

作者: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: IDOK

2: IDCANCEL

3 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 IDs

65300 to 65399: Standard tray menu items.

65400 to 65534: main menu items

消息號 | 含義

65300 | IDTRAYOPEN

65400 | IDFILERELOADSCRIPT, IDTRAYRELOADSCRIPT

65401 | IDFILEEDITSCRIPT, IDTRAYEDITSCRIPT

65402 | IDFILEWINDOWSPY, IDTRAYWINDOWSPY

65403 | IDFILEPAUSE, IDTRAYPAUSE

65404 | IDFILESUSPEND, IDTRAYSUSPEND

65405 | IDFILEEXIT, IDTRAYEXIT

65406 | IDVIEWLINES

65407 | IDVIEWVARIABLES

65408 | IDVIEWHOTKEYS

65409 | IDVIEWKEYHISTORY

65410 | IDVIEWREFRESH

65411 | IDHELPUSERMANUAL, IDTRAYHELP

65412 | IDHELPWEBSITE

註:一般而言在更新版本時消息號的用途不太可能發生變化,不過為了安全,在使用前最好明確其用途,否則可能發生意外情況。

實現的具體原理

在前面的腳本中我們應該注意到兩點:

  1. 開啟了對隱藏窗口的檢測
  2. 使用 AutoHotkey 類名獲取窗口

每個 AutoHotkey 運行時都有類名為 AutoHotkey 的主窗口(這個窗口為什麼稱為主窗口?請參閱 A_ScriptHwnd),與是否創建自定義 GUI 窗口(自定義窗口的類名為 AutoHotkeyGUI)、是否隱藏托盤圖標、是否打開調試窗口(這裡的調試窗口即是主窗口,未打開時為隱藏狀態)等無關。

說到這裡,我想起了一個有趣的事情:為什麼 AutoHotkey 被稱為模擬多線程呢?從這裡可以判斷出,每個腳本運行時至少有兩個線程:

  • 主線程,運行主窗口,負責接受消息、緩衝熱鍵及偽線程的中斷和切換等

    這裡的描述可能不太準確和全面,不過大體上可以這麼理解,其中的偽線程是指幫助中所說的線程概念(例如 Thread 中所描述的線程,注意幫助中除了 A_ScriptHwnd 外從未涉及到主線程)。
  • 腳本線程,實際執行腳本的線程,這不用多說了,它執行的就是我們寫的腳本。

從 Microsoft Spy++ 中可以看到,實際上每個腳本也只有兩個線程(你多開幾個熱鍵,多用幾個計時器並不會出現更多線程)。

小結

儘管未談到遊戲,不過主要內容前面都說完了。因此,要檢測 AutoHotkey 腳本的外掛只需在腳本啟動時及運行時定期執行:

DetectHiddenWindows, onWhile WinExist("ahk_class AutoHotkey") WinKill

遊戲不大可能用 AutoHotkey 寫,這裡的演示大家能看懂它的用途吧?不過這種檢測方法只是我猜測的,對於如 AutoIt、按鍵精靈這樣的腳本語言應該具有一定的可行性(如果是 C 語言這種估計沒那麼容易預防了)。正常的腳本如改鍵工具也無法例外了,有了解遊戲開發的朋友能否爆個真實情況。規避的方法也很簡單,自己下載源碼修改過類名編譯喔。

本文與遊戲關係不大,最開始的標題為腳本主窗口的妙用,不過這個標題是否更好呢?

TAG: |