roger 发表于 2020-9-1 10:32:54

【Windows代码注入教程】第一课 Windows消息的HOOK

  本文是我学习《逆向工程核心原理》一书dll注入部分的学习总结,如有理解不到位或错误的地方欢迎大家以评论的方式完善指正
  很多朋友一谈到Hook就觉得很神秘,其实Hook很好理解
  那么如何打开spy++呢?
  如下图所示
  Spy++ (SPYXX.EXE) 是一个基于 Win32 的实用工具,它提供系统的进程、线程、窗口和窗口消息的图形视图。使用 Spy++ 可以执行下列操作: 显示系统对象(包括进程、线程和窗口)之间关系的图形树。 搜索指定的窗口、线程、进程或消息。 查看选定的窗口、线程、进程或消息的属性。
  下图是打开后Spy++的界面
  我们在winuser.h中看一下这个函数的定义
  我们在微软的支持库里看一下这个函数的解释
  如果英文的看不懂,就看下文的中文解释
  HOOKPROClpfn不太好理解,单独说下,它的意思是钩子过程
  如果还看不懂就看下图的详细解释
  做一个练习,用键盘hook技术拦截notepad.exe进程的键盘消息,使之无法显示在记事本中
  首先看一下HookMain.exe的源码(关键代码处我已经做好了中文注释)

#include "stdio.h"
#include "conio.h"
#include "windows.h"

#defineDEF_DLL_NAME"KeyHook.dll"
#defineDEF_HOOKSTART"HookStart"
#defineDEF_HOOKSTOP"HookStop"

typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();

void main()
{
HMODULEhDll = NULL;
PFN_HOOKSTARTHookStart = NULL;
PFN_HOOKSTOPHookStop = NULL;
charch = 0;

    // 加载KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME);
    if( hDll == NULL )
    {
      printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
      return;
    }

    //获取导出函数地址
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

    //开始hook
HookStart();

    //用户输入‘q’停止hook
printf("press 'q' to quit!\n");
while( _getch() != 'q' );

    // 停止hook
HookStop();

    //卸载 KeyHook.dll
FreeLibrary(hDll);
}  接下来看KeyHook.dll的源码

#include "stdio.h"
#include "windows.h"

#define DEF_PROCESS_NAME"notepad.exe"

HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch( dwReason )
{
      case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;

      case DLL_PROCESS_DETACH:
break;
}

return TRUE;
}

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath = {0,};
char *p = NULL;

if( nCode >= 0 )
{
// bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) )//释放键盘按键时
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');

            // 比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序(或者下一个钩子)
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}

    // 如果不是notepad.exe,则调用CallNextHookEx函数,将消息传递给应用程序
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}

__declspec(dllexport) void HookStop()
{
if( g_hHook )
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif  我们再通过图来更深的了解一下HookMain.exe和keyhook.dll的关系
  接下来开始运行调试我们写好的HookMain.exe
  首先dll和注入器要在一个文件夹里(windows调用dll时会优先从同目录下寻找dll,所以不用放在system32文件夹里)
  用OD打开HookMain.exe

上图显示的是 HookMain . exe 的 EP 代码,它是典型的 VC + +启动函数,其中最受关注的是开始进行键盘钩取的部分。
查找核心代码有几种方法可以帮助我们找到关注的核心代码:
入口逐行跟踪。
入口检索相关 API 。
入口检索相关字符串。
第一种方法是程序无法正常运行或难以预测时使用的下策,此处略去不谈。这样就剩下后面 2 种方法(检索 API 或字符串)了。由于已经运行过 HookMain . exe 程序,我们知道了该程序的功能(键盘钩取)与输出的字符串,所以下面要使用检索字符串( “ Press ' q ' to quit ! " )的方法。引用该字符串代码的前后就是我们关注的代码。在 OllyDbg 的代码窗口中,选择鼠标右键菜单中的 search for :一 All referenced text strings 项


双击进去,到达main函数

  出现如下代码
  执行完dll中的键盘hook函数后,键盘钩子就已经安装好了
  用Process Explorer查看我们装好的钩子
  然后我们运行到40104D处,让控制台输出字符串
  执行40104D处的代码,程序跑起来了
  不管按什么键盘按键,记事本什么都不显示,因为我们发出的键盘消息被拦截,它现在什么都收不到(可怜的记事本~~~~~~)
  检索注入keyhook.dll的所有进程(查找——查找句柄或DLL)
  输入要查找的dll,可以看到我们所有进程都被注入了keyhook.dll
  因为我们安装的是全局钩子(前文讲SetWindowsHookEx参数时已经说明白了,不懂回去看)
  源码中也可以看到为什么我们设置的全局钩子但只对notepad.exe有效了
  接下来调试Notepad.exe进程内的KeyHook.dll
  我使用学破解论坛的2.0OD
  我们在段首下CC断点
  然后在nopad.exe中随便输入一个字符
  程序会断下
  看堆栈可以看到充当回调函数的是user32.dll中的函数,user32.dll也就是参数HOOKPROCl pfn的实际过程
  总结一下上述操作的顺序
  好了,历经6个小时,终于写完了这一课,大家一定要自己亲手操作来学习,切不可走马观花,下一课会讲DLL注入,比这一课难很多,如果这节课没有理解,DLL注入的3种方法更不可能理解了。
            
页: [1]
查看完整版本: 【Windows代码注入教程】第一课 Windows消息的HOOK