金山詞霸2003導致我的程序堆棧溢出錯誤

最近發現金山詞霸屏幕取詞功能導致連連看在ET內執行的時候堆棧溢出
而連連看單機版則無此問題。鬱悶異常,分析金山詞霸的取詞功能如下:

1 屏幕抓詞

  屏幕抓詞(或者叫動態翻譯)是指隨着鼠標的移動,軟件能夠隨時獲知屏幕上鼠標位置的單詞或漢字,並翻譯出來提示用戶。它對於上網瀏覽、在線閱讀外文文章等很有幫助作用,因此許多詞典軟件都提供了屏幕抓詞功能。
  屏幕抓詞的關鍵是如何獲得鼠標位置的字符串,Windows的動態鏈接和消息響應機制爲之提供了實現途徑。 概括地說,主要通過下面的幾個步驟來取得屏幕上鼠標位置的字符串:
  . 代碼攔截:Windows以DLL方式提供系統服務,可以方便地獲取Windows字符輸出API的地址,修改其入口代碼,攔截應用程序對它們的調用。

  .  鼠標HOOK:安裝WH—MOUSEPROC類型的全局鼠標HOOK過程,監視鼠標在整個屏幕上的移動。
  .   屏幕刷新:使鼠標周圍一塊區域無效,並強制鼠標位置的窗口刷新屏幕輸出。窗口過程響應WM—NCPAINT和WM—PAINT消息,調用ExtTextOut/TextOut等字符輸出API更新無效區域裏面的字符串。這些調用被我們截獲,從堆棧裏取得窗口過程傳給字符API的參數,如字符串地址、長度、輸出座標、HDC、裁剪區等信息。

2 取詞的過程,

          0 判斷鼠標是否在一個地方停留了一段時間
          1 取得鼠標當前位置
          2 以鼠標位置爲中心生成一個矩形
          3 掛上API鉤子
          4 讓這個矩形產生重畫消息
          5 在鉤子裏等輸出字符
          6 計算鼠標在哪個單詞上面,把這個單詞保存下來
          7 如果得到單詞則摘掉API鉤子,在一段時間後,無論是否得到單詞都摘掉API鉤子
          8 用單詞查詞庫,顯示解釋框。

3 出錯原因(轉)

開着金山詞霸瀏覽網頁的時候經常就會瀏覽器自動關閉,實在煩人。剛纔又遇到,就稍微分析了一下,覺得因爲我們也經常使用鉤子,可能會有借鑑,就把分析結果稍微寫寫,讓大家以後編程少犯這樣的錯誤。
    翻譯軟件等關鍵技術之一就是攔截操作系統的顯示函數,從中抓到顯示的單詞,這點大家都明白不用再多說。比如金山詞霸會攔截系統庫GDI32.DLL的輸出函數ExtTextOutA等,攔截方法是找到這個函數地址,在這入口填寫一個JMP 指令,跳轉到金山詞霸的一個入口,當然其也保存了這個入口開始被JMP指令覆蓋的幾個字節。金山詞霸的這個入口裏面又要用這個函數,所以其得到控制權後又恢復了ExtTextOutA這個函數開始的指令,進行調用。這種不好的鉤子處理辦法就埋下了禍根。可能是其內部信號標記等處理不好,某種情況下鉤子想調用恢復後的ExtTextOutA函數的時候,但可能沒有恢復成功,就又進入了金山詞霸的鉤子,這樣就導致循環了,堆棧迅速消耗,最終因爲堆棧消耗完再進行堆棧操作的時候出現非法操作而導致程序關閉退出。
    這就告訴我們如果鉤子裏面要調用要鉤的函數本身,最好要錯過鉤子。比如在函數入口用寫JMP指令做鉤子,那麼再調用的時候最好調用其下面的哪個入口,或者鉤子種在上面調用這個函數的指令那。要不就要保證恢復入口指令一定正確。再就是注意重入問題,就是鉤子裏面再調用別的函數的時候可能別的函數會用到鉤子鉤的函數,那你就得保證你的代碼是可重入的。其實寫過病毒的人恐怕都或多或少接觸了重入問題。

 

程序1: call dword ptr  [ExtTextOutA]

要鉤的函數:
GDI32.DLL
ExtTextOutA:
               push ebp
               ...
               call  ExtTextOutW
               ....         
               ret 

假設環境如上面,鉤子方案可以:

   1、改寫程序1的call dword ptr [ExtTextOutA]中[]內存的值,指向鉤子,鉤子裏面要再用到ExtTextOutA的話正常調用GDI32.DLL中的這個函數就是了。這個方法缺點可能就是要修改地方多。
   2、修改GDI32.DLL的函數ExtTextOutA開始指令,指向鉤子,但鉤子裏面再調用這個函數就用裏層的ExtTextOutW(這兒是假設的,我也不知道這裏面有什麼接口)。
   3、就是金山詞霸用的修改開始指令,要用的時候就恢復開始指令後調用。
  (2、3兩點有個不好就是要鉤的函數開始一定要有幾個這個函數體的指令,這幾個指令這兒還不能是別的什麼函數入口,這點對一般函數沒問題,對於一些特殊函數恐怕就不行了)。
   4、修改GDI32.DLL的函數引出表的ExtTextOutA的值,指向鉤子,這樣加載別的DLL的時候用到此函數操作系統就會自動把鉤子地址填寫,這樣就不用程序操心去改寫第1個方法要改寫的地方了。缺點嘛就是先加載了的DLL無效,再就是恢復麻煩。
   5、......
   大家可以綜合考慮使用自己喜歡的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章