?? lion-tutorial24.htm
字號:
<html>
<head>
<link rel="stylesheet" href="../../asm.css">
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>Iczelion's win32 asm tutorial</title>
</head>
<body bgcolor="#FFFFFF" background="../../images/back01.jpg">
<p align="center">Tutorial 24: Windows Hooks</p>
<hr size="1">
We will learn about Windows hooks in this tutorial. Windows hooks are very powerful.
With them, you can poke inside other processes and sometimes alter their behaviors.
<br>
Download the example <a href="files/tut24.zip">here</a>.
<h3> Theory:</h3>
Windows hooks can be considered one of the most powerful features of Windows.
With them, you can trap events that will occur, either in your own process or
in other processes. By "hooking", you tell Windows about a function, filter function
also called hook procedure, that will be called everytime an event you're interested
in occurs. There are two types of them: local and remote hooks.
<ul>
<li> Local hooks trap events that will occur in your own process.</li>
<li> Remote hooks trap events that will occur in other process(es). There are
two types of remote hooks</li>
<ul>
<li> thread-specific traps events that will occur in a specific thread
in other process. In short, you want to observe events in a specific thread
in a specific process.</li>
<li> system-wide traps all events destined for all threads in all processes
in the system.</li>
</ul>
</ul>
When you install hooks, remember that they affect system performance. System-wide
hooks are the most notorious. Since ALL related events will be routed through
your filter function, your system may slow down noticeably. So if you use a system-wide
hook, you should use it judiciously and unhook it as soon as you don't need it.
Also, you have a higher chance of crashing the other processes since you can meddle
with other processes and if something is wrong in your filter function, it can
pull the other processes down to oblivion with it. Remember: Power comes with
responsibility. <br>
You have to understand how a hook works before you can use it efficiently. When
you create a hook, Windows creates a data structure in memory, containing information
about the hook, and adds it to a linked list of existing hooks. New hook is added
in front of old hooks. When an event occurs, if you install a local hook, the
filter function in your process is called so it's rather straightforward. But
if it's a remote hook, the system must inject the code for the hook procedure
into the address space(s) of the other process(es). And the system can do that
only if the function resides in a DLL. Thus , if you want to use a remote hook,
your hook procedure must reside in a DLL. There is two exceptions to this rule:
journal record and journal playback hooks. The hook procedures for those two hooks
must reside in the thread that installs the hooks. The reason why it must be so
is that: both hooks deal with the low-level interception of hardware input events.
The input events must be recorded/playbacked in the order they appeared. If the
code of those two hooks is in a DLL, the input events may scatter among several
threads and it is impossible to know the order of them. So the solution: the hook
procedure of those two hooks must be in a single thread only i.e. the thread that
installs the hooks. <br>
There are 14 types of hooks:
<ul>
<li> <b>WH_CALLWNDPROC</b> called when SendMessage is called</li>
<li> <b>WH_CALLWNDPROCRET</b> called when SendMessage returns</li>
<li> <b>WH_GETMESSAGE</b> called when GetMessage or PeekMessage
is called</li>
<li> <b>WH_KEYBOARD</b> called when GetMessage or PeekMessage retrieves
WM_KEYUP or WM_KEYDOWN from the message queue</li>
<li> <b>WH_MOUSE</b> called when GetMessage or PeekMessage retrieves a
mouse message from the message queue</li>
<li> <b>WH_HARDWARE</b> called when GetMessage or PeekMessage retrieves some
hardware message that is not related to keyboard or mouse.</li>
<li> <b>WH_MSGFILTER </b> called when a dialog box, menu or scrollbar is
about to process a message. This hook is local. It's specifically for those
objects which have their own internal message loops.</li>
<li> <b>WH_SYSMSGFILTER</b> same as WH_MSGFILTER but system-wide</li>
<li> <b>WH_JOURNALRECORD</b> called when Windows retrieves message from
the hardware input queue</li>
<li> <b>WH_JOURNALPLAYBACK</b> called when an event is requested from
the system's hardware input queue.</li>
<li> <b>WH_SHELL</b> called when something interesting about the shell
occurs such as when the task bar needs to redraw its button.</li>
<li> <b>WH_CBT</b> used specifically for computer-based training (CBT).</li>
<li> <b>WH_FOREGROUNDIDLE</b> used internally by Windows. Little use for general
applications</li>
<li> <b>WH_DEBUG</b> used to debug the hooking procedure</li>
</ul>
Now that we know some theory, we can move on to how to install/uninstall the hooks.
<br>
To install a hook, you call SetWindowsHookEx which has the following syntax:
<blockquote><b>SetWindowsHookEx proto HookType:DWORD, pHookProc:DWORD, hInstance:DWORD,
ThreadID:DWORD</b>
<ul>
<li> HookType is one of the values listed above, e.g., <b>WH_MOUSE</b>, <b>WH_KEYBOARD</b></li>
<li> pHookProc is the address of the hook procedure that will be called to
process the messages for the specified hook. If the hook is a remote one,
it must reside in a DLL. If not, it must be in your process.</li>
<li> hInstance is the instance handle of the DLL in which the hook procedure
resides. If the hook is a local one, this value must be NULL</li>
<li> ThreadID is the ID of the thread you want to install the hook to
spy on. This parameter is the one that determines whether a hook is local
or remote. If this parameter is NULL, Windows will interpret the hook as
a system-wide remote hook that affects all threads in the system. If you
specify the thread ID of a thread in your own process, this hook is a local
one. If you specify the thread ID from other process, the hook is a thread-specific
remote one. There are two exceptions to this rule: <b>WH_JOURNALRECORD</b>
and <b>WH_JOURNALPLAYBACK</b> are always local system-wide hooks that are
not required to be in a DLL. And <b>WH_SYSMSGFILTER</b> is always a system-wide
remote hook. Actually it is identical to <b>WH_MSGFILTER</b> hook with ThreadID==0.</li>
</ul>
If the call is successful, it returns the hook handle in eax. If not, NULL is
returned. You must save the hook handle for unhooking later.</blockquote>
You can uninstall a hook by calling<b> UnhookWindowsHookEx</b> which accepts only
one parameter, the handle of the hook you want to uninstall. If the call succeeds,
it returns a non-zero value in eax. Otherwise, it returns NULL. <br>
Now that you know how to install/uninstall hooks, we can examine the hook procedure.
<br>
The hook procedure will be called whenever an event that is associated with the
type of hook you have installed occurs. For example, if you install <b>WH_MOUSE</b>
hook, when a mouse event occurs, your hook procedure will be called. Regardless
of the type of hook you installed, the hook procedure always has the following
prototype:
<ul>
<b>HookProc proto nCode:DWORD, wParam:DWORD, lParam:DWORD</b> <br>
<ul>
<li> nCode specifies the hook code.</li>
<li> wParam and lParam contain additional information about the event</li>
</ul>
</ul>
HookProc is actually a placeholder for the function name. You can name it anything
you like so long as it has the above prototype. The interpretation of nCode, wParam
and lParam is dependent on the type of hook you install. So as the return value
from the hook procedure. For example:
<blockquote><b>WH_CALLWNDPROC</b>
<ul>
<li> nCode can be only HC_ACTION which means there is a message sent to a
window</li>
<li> wParam contains the message being sent, if it's not zero</li>
<li> lParam points to a CWPSTRUCT structure</li>
<li> return value: not used, return zero</li>
</ul>
<b>WH_MOUSE</b>
<ul>
<li> nCode can be HC_ACTION or HC_NOREMOVE</li>
<li> wParam contains the mouse message</li>
<li> lParam points to a MOUSEHOOKSTRUCT structure</li>
<li> return value: zero if the message should be processed. 1 if the message
should be discarded.</li>
</ul>
</blockquote>
The bottom line is: you must consult your win32 api reference for details about
the meanings of the parameters and return value of the hook you want to install.
<br>
Now there is a little catch about the hook procedure. Remember that the hooks
are chained in a linked list with the most recently installed hook at the head
of the list. When an event occurs, Windows will call only the first hook in the
chain. It's your hook procedure's responsibility to call the next hook in the
chain. You can choose not to call the next hook but you'd better know what you're
doing. Most of the time, it's a good practice to call the next procedure so other
hooks can have a shot at the event. You can call the next hook by calling <b>CallNextHookEx</b>
which has the following prototype:
<blockquote><b>CallNextHookEx proto hHook:DWORD, nCode:DWORD, wParam:DWORD, lParam:DWORD</b>
<ul>
<li> hHook is your own hook handle. The function uses this handle to traverse
the linked list and search for the hook procedure it should call next.</li>
<li> nCode, wParam and lParam you can just pass those three values you
receive from Windows to CallNextHookEx.</li>
</ul>
</blockquote>
An important note about remote hooks: the hook procedure must reside in a DLL
which will be mapped into other processes. When Windows maps the DLL into other
processes, it will not map the data section(s) into the other processes. In short,
all processes share a single copy of code but they will have their own private
copy of the DLL's data section! This can be a big surprise to the unwary. You
may think that when you store a value into a variable in the data section of a
DLL, that value will be shared among all processes that load the DLL into their
process address space. It's simply not true. In normal situation, this behavior
is desirable since it provides the illusion that each process has its own copy
of the DLL. But not when Windows hook is concerned. We want the DLL to be identical
in all processes, including the data. The solution: you must mark the data section
as shared. You can do this by specifying the section(s) attribute in the linker
switch. For MASM, you need to use this switch:
<blockquote><b>/SECTION:<section name>, S</b></blockquote>
The name of the initialized data section is .data and the uninitialized data is
.bss. For example if you want to assemble a DLL which contains a hook procedure
and you want the uninitialized data section to be shared amoung processes, you
must use the following line:
<blockquote><b>link /section:.bss,S /DLL /SUBSYSTEM:WINDOWS ..........</b></blockquote>
S attribute marks the section as shared.
<h3> Example:</h3>
There are two modules: one is the main program which will do the GUI part and
the other is the DLL that will install/uninstall the hook.
<p>;--------------------------------------------- This is the source code of the
main program -------------------------------------- <br>
<b>.386</b> <br>
<b>.model flat,stdcall</b> <br>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =