zoukankan      html  css  js  c++  java
  • Windows消息钩取

    @author: dlive

    @date: 2016/12/19

    0x01 SetWindowsHookEx()

    HHOOK SetWindowsHookEx(
    	int idHook,   //hook type
    	HOOKPROC lpfn, //hook procedure(回调函数)
    	HINSTANCE hMod, //hook procedure所属的DLL句柄
    	DWORD dwThreadId //想要挂钩的线程ID
    );
    

    使用SetWindowsHookEx() API可以轻松实现消息钩子。用于将制定的“钩子过程”注册到钩链中。无论在DLL内部还是外部都可以调用。(下面的例子是在DLL内部调用的)

    hook procedure(钩子过程) 是由操作系统调用的回调函数,安装消息钩子时,钩子过程需要存在于某个DLL内部,且该DLL的instance handle(示例句柄)即是hMod

    若dwThreadID参数被设置为0,则安装的钩子为全局钩子,它会影响到运行中的所有进程

    0x02 HookMain.exe

    #include "stdio.h"
    #include "conio.h"
    #include "windows.h"
    
    #define	DEF_DLL_NAME		"KeyHook.dll"
    #define	DEF_HOOKSTART		"HookStart"
    #define	DEF_HOOKSTOP		"HookStop"
    
    typedef void (*PFN_HOOKSTART)();
    typedef void (*PFN_HOOKSTOP)();
    
    void main()
    {
    	HMODULE			hDll = NULL;
    	PFN_HOOKSTART	HookStart = NULL;
    	PFN_HOOKSTOP	HookStop = NULL;
    	char			ch = 0;
    
    	//加载DLL
    	hDll = LoadLibraryA(DEF_DLL_NAME);
        if( hDll == NULL )
        {
            printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
            return;
        }
    	
      	//从DLL中获取函数地址
    	HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
    	HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
    	
      	//执行HookStart函数
    	HookStart();
    
    	printf("press 'q' to quit!
    ");
    	while( _getch() != 'q' )	;
    
    	HookStop();
    	
      	//卸载DLL
    	FreeLibrary(hDll);
    }
    

    0x03 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;
    
    //DLL中的Main函数
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
    {
    	switch( dwReason )
    	{
            //DLL加载的时候
            case DLL_PROCESS_ATTACH:
            	//钩子过程(KeyboardProc)所属DLL句柄,即本DLL
    			g_hInstance = hinstDLL;
    			break;
    		//DLL卸载的时候
            case DLL_PROCESS_DETACH:
    			break;	
    	}
    
    	return TRUE;
    }
    
    //钩子过程
    //MSDN对KeyboardProc的定义:https://msdn.microsoft.com/en-us/library/ms644984(v=vs.85).aspx
    //nCode :HC_ACTION(0), HC_NOREMOVE(3)
    //wParam : 虚拟键值(virtual key code),对于键盘而言a和A具有相同的虚拟键值
    //lParam 额外信息
    LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	char szPath[MAX_PATH] = {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) )
                  	//终止KeyboardProc函数,意味着截获并删除信息
    				return 1;
    		}
    	}
    	//Passes the hook information to the next hook procedure in the current hook chain.
    	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
    }
    
    #ifdef __cplusplus
    //关于为何使用ifdef __cplusplus
    //参考:http://blog.csdn.net/miyunhong/article/details/4589541
    extern "C" {
    #endif
      	//DLL的导出函数
    	__declspec(dllexport) void HookStart()
    	{
          	//设置消息钩子
          	//WH_KEYBOARD 钩子类型 
          	//参考:https://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx#wh_keyboardhook
          	//KeyboardProc 钩子过程(回调函数)
          	//g_hInstance 钩子过程所在DLL的句柄
          	//0 想要挂钩的线程ID,若为0则为全局Hook
    		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
    

    0x04 调试KeyLogger.dll

    首先运行notepad.exe

    然后再OD的”调试选择“中选择Event->Break on new moudle(DLL)

    开启该选项后,每当有新的DLL装入被调试进程时就会停止调试

    0x05 参考资料

    1. MSDN Hook

      https://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx

    注:本博客文章转载需带上原文链接
  • 相关阅读:
    Redis常用操作命令
    redis-sentinel.conf配置项详解
    Kafka常用命令
    go modules的使用姿势
    GO语言密码加解密(bcrypt)
    ssh-copy-id 秘钥分发报错
    k8s 命令提示
    算法与数据结构(持续更新)
    【spring】 @PostConstruct注解
    Spring Boot 整合Redis
  • 原文地址:https://www.cnblogs.com/dliv3/p/6220399.html
Copyright © 2011-2022 走看看