Lịch sử về hooking trên windows

Lịch sử về hooking trên windows

Nội dung Text: Hướng dẫn về kỹ thuật hook

  1. Có bao giờ các bạn đặt ra câu hỏi là tại sao chúng ta có thể gõ tiếng Việt được trong  Microsoft Word và các phần mềm soạn thảo văn bản khác hay không? Rõ ràng là chúng  ta không thể can thiệp vào mã của Microsoft Word để sửa thành tiếng Việt khi chúng ta  muốn soạn thảo văn bản tiếng Việt. Vậy thì tại sao các phần mềm như VietKey, VNI­ TanKy lại có thể làm được điều này? Câu trả lời là sử dụng các Hook. Trong bài viết này,  chúng ta sẽ cùng tìm hiểu xem Hook là gì mà có thể làm được điều thần kỳ như vậy!  Truớc khi chúng ta tìm hiểu về Hook , chúng ta nhắc lại một chút về quá trình xử lý thông  điệp của hệ điều hành Windows  Quá trình xử lý thông điệp của Windows diễn ra như sau : Đầu tiên từ các hành động của  nguời dùng như là : click chuột, nhấn phím, .. thì hệ điều hành sẽ chuyển các hành động  tương ứng thành các thông điệp (message). Rồi sau đó Windows đẩy các message này  vào hàng đợi của hệ thống (system queue) và từ system queue các message đuợc  chuyển cho các hàng đợi của ứng dụng (application queue). Từ lúc này các ứng dụng sẽ  lấy các message này trong hàng đợi ứng dụng của mình để xử lý (thông qua các vòng lặp  chờ thông điệp ­ message loop).  1. Hook là gì ?  Hook là cơ chế mà nhờ đó một hàm có thể chặn các sự kiện (message, mouse actions,  keystrokes ) trước khi chúng được gửi đến hàng đợi của ứng dụng. Các hàm này có thể  thực hiện một số thao tác trên sự kiện, và trong một vài trường hợp có thể định nghĩa lại  hoặc hủy bỏ sự kiện mà nó chặn được. Một điểm quan trọng cần lưu ý là các hàm này  được gọi bởi chính Windows chứ không phải bởi ứng dụng của chúng ta.  Windows hỗ trợ nhiều loại hook khác nhau, mỗi loại nhắm đến việc chặn bắt một loại  thông điệp cụ thể nào đó. Ví dụ, một ứng dụng có thể sử dụng WH_KEYBOARD để giám  sát sự di chuyển của thông điệp bàn phím trong hệ thống. Nhờ loại hook này mà một  chương trình có thể can thiệp vào và tạo ra khả năng gõ tiếng Việt khi soạn thảo văn bản.  Một loại hook khác là WH_MOUSE cho phép theo dõi các thông điệp liên quan đến hoạt  động của con chuột.  Hình trên mô tả quá trình xử lý thông điệp của Windows khi có sử dụng các Hook .Thì 
  2. như trên hình vẽ chúng ta có thể thấy rõ ràng rằng ,một khi chúng ta sử dụng các Hook  thì các Hook này sẽ được đặt nằm giữa System Queue và Application Queue .  2. Cơ chế hoạt động của Hook  Hệ thống duy trì một chuỗi hook (hook chain) cho mỗi loại hook. Mỗi chuỗi này là một  danh sách liên kết các con trỏ đặt biệt, con trỏ này chính là các hàm callback của ứng  dụng có sẵn, nó còn được gọi là hàm hook (hàm lọc, filter function)  Khi có một thông điệp được sinh ra thuộc một loại hook nào đó, nó sẽ được hệ thống đẩy  đi vào hàm hook đầu tiên trong chuỗi, lần lượt từng hàm một (qua tất cả các hook trong  chuỗi). Công việc của hàm hook có thể phức tạp hay đơn giản tùy thuộc vào từng loại  hook. Hàm hook cho một số loại chỉ có thể giám sát, số khác có thể sửa đổi thông điệp  hoặc dừng lại việc xử lý thông điệp trên chuỗi hook trước khi chúng đến các hook tiếp  theo hoặc đến cửa sổ đích.  3. Ứng dụng của Hook  ­ Cho phép tạo ra các chương trình hỗ trợ gõ tiếng Việt như : Vietkey  ­ Cho phép tạo ra các chuơng trình Test tự động phần mềm (bằng cách phát sinh các sự  kiện phím, chuột giống như người dùng đang nhập vào).  ­ Cho phép thay đổi giao diện các ứng dụng đang chạy.  ­ Cho phép xem phần trợ giúp của các ứng dụng bằng việc nhấn một phím nào đó, ví dụ  như nhấn F1 chẳng hạn  ­ Và nhiều ứng dụng khác nữa tùy vào trí tưởng tượng của các bạn !…  4 . Cài đặt Hook  Giao diện lập trình ứng dụng (API) của Windows cung cấp 3 hàm để thao tác với hook :  • SetWindowsHookEx  • UnhookWindowsHookEx 
  3. • CallNextHookEx  a) Cài đặt một Filter Function vào chuỗi các Filter Function của một hook  Tác vụ này được thực hiện thông qua hàm SetWindowsHookEx, khai báo của hàm này  như sau :  HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn,  HINSTANCE hMod, DWORD dwThreadId);  Ý nghĩa của từng tham số :  idHook: Xác định loại hook mà ta muốn cài đặt, tham số này có thể là một trong các giá  trị sau :  • WH_CALLWNDPROC : đặt một thủ tục hook quản lý các thông điệp trước lúc hệ thống  gởi chúng tới cửa sổ đích.  • WH_CALLWNDPROCRET : đặt một thủ tục hook quản lý các thông điệp sau khi chúng  được xử lý bởi thủ tục cửa sổ đích.  • WH_CBT : đặt một thủ tục hook nhận những thông báo có ích tới ứng dụng huấn luyện  trên cơ sở tính toán (CBT).  • WH_DEBUG : đặt một thủ tục hook có ích cho việc debug những thủ tục hook khác.  • WH_FOREGROUNDIDLE : đặt một thủ tục hook sẽ được gọi khi thread foreground của  ứng dụng sẽ trở thành không dùng đến. Hook này có ích cho hoạt động những nhiệm vụ  (task) độ ưu tiên thấp trong thời gian không được dùng đến.  • WH_GETMESSAGE : đặt một thủ tục hook quản lý các thông điệp được post tới hàng  đợi thông điệp.  •WH_JOURNALPLAYBACK : đặt một thủ tục hook post những thông điệp được ghi trước  đó bởi thủ tục hook WH_JOURNALRECORD.  •WH_JOURNALRECORD : đặt một thủ tục hook ghi những thông điệp đầu vào được post  tới hàng thông điệp hệ thống. Hook này có ích cho việc ghi các macro. 
  4. • WH_KEYBOARD : đặt một thủ tục hook quản lý các thông điệp keystroke.  • WH_MOUSE : đặt một thủ tục hook quản lý các thông điệp chuột.  • WH_MSGFILTER: đặt một thủ tục hook quản lý các thông điệp được kết sinh như là một  kết quả cuả sự kiện đầu vào ở trong dialog box, message box, menu hay scroll bar.  • WH_SYSMSGFILTER : đặt một ứng dụng các thông điệp được kết sinh như là kết quả  của một sự kiện đầu vào ở trong dialog box, message box, menu hay scroll bar. Thủ tục  hook quản lý những thông điệp này cho tất cả các ứng dụng trong hệ thống.  Mỗi giá trị trên xác định một loại hook mà ta muốn cài đặt, mỗi loại hook có một ý nghĩa  và tình huống sử dụng khác nhau.  lpfn : Địa chỉ của Filter Function mà ta muốn gắn với hook.  hMod : Handle của module chứa Filter Function. Nếu ta cài đặt một hook cục bộ (nghĩa là  sự thực thi của Filter Function chỉ ảnh hưởng đối với tiến trình cài đặt hook), tham số này  phải là NULL. Còn nếu chúng ta muốn có một hook với phạm vi toàn hệ thống (tức là mọi  tiến trình đang hiện hữu đều chịu ảnh hưởng bởi Filter Function của chúng ta), tham số  này sẽ là Handle của DLL chứa Filter Function.  dwThreadID : Định danh của thread ứng với hook đang được cài đặt . Nếu tham số này là  một số khác 0, Filter Function được gắn với hook chỉ được gọi trong ngữ cảnh của một  thread xác định. Còn nếu dwThreadID = 0, Filter Function sẽ có phạm vi toàn hệ thống,  và dĩ nhiên, nó sẽ được gọi trong ngữ cảnh của bất kỳ thread nào đang tồn tại trên HĐH.  Có thể sử dụng hàm GetCurrentThreadId để lấy được handle của thread muốn cài đặt  hook.  Một hook có thể được sử dụng ở mức hệ thống, ở mức cục bộ, hoặc ở cả hai mức vừa  nêu. Bảng sau mô tả các loại hook cùng tầm ảnh hưởng của nó :  WH_CALLWNDPROC  Thread , Global  WH_CALLWNDPROCRET  Thread , Global 
  5. WH_CBT  Thread , Global  WH_DEBUG  Thread , Global  WH_FOREGROUNDIDLE  Thread , Global  WH_GETMESSAGE  Thread , Global  WH_JOURNALPLAYBACK  Global  WH_JOURNALRECORD  Global  WH_KEYBOARD  Thread , Global  WH_MOUSE  Thread , Global  WH_MSGFILTER  Thread , Global  WH_SYSMSGFILTER  Global  Với một loại hook xác định, hook cục bộ sẽ được gọi trước, sau đó là hook toàn cục.  b) Gỡ bỏ một Filter Function ra khỏi chuỗi các Filter Function của một hook  Windows cung cấp hàm UnhookWindowsHookEx giúp chúng ta thực hiện việc này. Khai  báo của nó như sau : 
  6. BOOL UnhookWindowsHookEx( HHOOK hhk);  Tham số : hhook chỉ ra hàm hook được dỡ bỏ . Đây là giá trị được trả vể bởi hàm  SetWindowsHookEx khi hàm Hook được cài đặt.  Chú ý : Hàm UnhookWindowsHookEx phải được sử dụng trong sự kết hợp với hàm  SetWindowsHookEx.  c) Chi tiết về Filter Function  Filter Function là một hàm được gắn với loại hook mà chúng ta muốn cài đặt. Hàm này  được gọi bởi hệ điều hành Windows chứ không được gọi bởi ứng dụng, đây cũng là lý do  mà người ta thường gọi nó là “Callback Function”. Tuy nhiên , để thống nhất về mặt thuật  ngữ, từ nay về sau chúng ta vẫn gọi nó là Filter Function.  Tất cả các Filter Function đều có dạng sau :  LRESULT CALLBACK FilterFunc(int nCode, WPARAM wParam, LPARAM lParam);   Ở đây “FilterFunc” chỉ là tên hàm tượng trưng, khi cài đặt, Filter Function sẽ có tên bất kỳ  theo ý của người lập trình .  Ý nghĩa của từng tham số truyền cho hàm :  nCode : tham số này thường được gọi là “hook code”, Filter Function sử dụng giá trị này  để quyết định cách thức xử lý đối với sự kiện. Giá trị của hook code tùy thuộc vào từng  loại hook cụ thể, và mỗi loại hook sẽ có tập hợp những giá trị hook code đặc trưng của  riêng mình. Có một quy luật mà dường như các Filter Function của mọi loại hook cần tuân  thủ : Khi Windows truyền cho hàm giá trị hook code âm, Filter Function không được xử lý  sự kiện mà phải gọi hàm CallNextHookEx với chính những tham số mà hệ điều hành  truyền cho nó. Sau đó, nó phải trả về giá trị được trả về bởi hàm CallNextHookEx.  wParam, lParam: Đây là những thông tin cần thiết cho Filter Function trong quá trình xử lý  sự kiện. Các giá trị này sẽ có ý nghĩa khác nhau tuỳ thuộc vào từng loại hook. Ví dụ ,  Filter Function gắn với hook WH_KEYBOARD sẽ nhận mã phím ảo (Virtual­Key Code) từ  wParam, đồng thời có được từ lParam thông tin mô tả trạng thái của bàn phím khi sự kiện  gõ phím xảy ra. 
  7. d) Gọi Filter Function kế tiếp trong chuỗi các Filter Function  Khi một hook được cài đặt, Windows gọi hàm đầu tiên trong chuỗi các Filter Function, và  kể từ thời điểm này, trách nhiệm Windows không còn nữa. Filter Function hiện hành phải  đảm bảo với hệ thống là có được lời gọi đến hàm kế tiếp trong chuỗi các Filter Function.  Bởi lẽ, có thể có một ứng dụng khác cũng cài đặt cùng loại hook để thi hành một số tác  vụ nào đó, và nếu như ta không cho Filter Function của ứng dụng này tham gia xử lý sự  kiện, sẽ có vấn đề rắc rối xảy ra. Vấn đề sẽ càng trở nên nghiêm trọng nếu ứng dụng này  là một chương trình thuộc hệ thống, và rõ ràng sẽ không có gì đảm bảo cho sự an toàn  của hệ thống chúng ta. Để giải quyết vấn đề trên, hãy sử dụng hàm CallNextHookEx,  khai báo của nó như sau :  LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM  lParam );  hhk : là handle của hook hiện hành, giá trị này có thể lấy được từ hàm  SetWindowsHookEx khi cài đặt hook.  nCode : chỉ định hook code để gởi đến hook kế tiếp. Hàm xử lý hook dùng giá trị này để  chỉ định xử lý thông điệp được gởi từ hook như thế nào .  wParam: chỉ định 16 bits thông tin mở rộng của thông điệp.  lParam: chỉ định 32 bits thông tin mở rộng của thông điệp.  Giá trị trả về : giá trị trả về là kết quả của quá trình xử lý và tùy thuộc vào thông số  nCode  Trong một số tình huống, Filter Function hiện hành có thể không muốn chuyển sự kiện  cho Filter Function khác trong cùng một chuỗi. Lúc này, nếu loại hook chúng ta đang cài  đặt cho phép huỷ bỏ sự kiện, và Filter Function của chúng ta cũng có cùng quyết định là  hủy bỏ, nó sẽ không phải gọi hàm CallNextHookEx.  5. Chương trình minh họa  Để hiểu rõ hơn về Hook ,các bạn có thể xem một ví dụ đơn giản về sử dụng Hook. Trong  ví dụ này chúng ta sẽ cài đặt một Hook ,cho phép đọc thông tin về menu bar chuẩn của  bất kỳ chương trình ứng dụng nào và lưu các thông tin này thành một tập tin resource (để 
  8. sau đó ta có thể dễ dàng sử dụng trong chương trình của mình!)  Trong ví dụ này chúng ta sẽ sử dụng Hook WH_GETMESSAGE để chặn bắt các thông  điệp gửi đến hàng đợi thông điệp . Và khi chương trình của chúng ta bắt được thông điệp  WM_NCLBUTTONDOWN (thông điệp này đuợc phát sinh khi chúng ta click chuột lện  thanh tiêu đề của cửa sổ) , chương trình sẽ cho mở hộp thoại Save As để cho nguời dùng  chọn đường dẫn và đặt tên cho file resource sẽ được tạo ra .Và lúc này chuơng trình sẽ  lưu toàn bộ nội dung của menubar xuống thành file resource mà chúng ta đã đặt tên.  * Trong ví dụ này vì ta muốn lấy thông tin về menu bar của tất cả ứng dụng trên Windows  nên ta phải sử dụng một tập tin .DLL để lập một Hook loại toàn cục (còn các hook loại  cục bộ chỉ cho phép chặn các thông điệp trong nội bộ ứng dụng có cài đặt Hook mà  thôi).