Windows 消息钩取

一、回调函数概念理解

调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来

打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是
打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头
上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式
是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动
作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register 
a callback function

这里写图片描述

回调函数举例

#include<stdio.h>
typedef int (*p)(int ,int);
int call(int a,int b,p p)//系统的dll定义参数
{
    return (*p)(a,b);
} 
int add(int a,int b)//用户自己写的实现函数add
{
    return a+b;
}
int sub(int a,int b)//用户自己写的实现函数add
{
    return a-b;
}
int main()
{
    printf("%d",call(1,1,add));
    printf("\n");
    printf("%d",call(1,1,sub));
    return 0;
}

二、进程 线程 句柄概念

  1. 进程

~进程是执行中的程序:程序是一段描述指令的文本,是一个静态的概念,把这段指令运行起来,每次运行就得到了一个进程,进程是动态的概念;操作系统会为进程分配资源。
~ 进程是资源分配的基本单位:也就是说操作系统会为不同的进程分配不同的资源,如Window句柄、文件系统句柄、内核对象、虚拟内存等,我们可以说操作系统把Window句柄分配给某个进程,而不能说操作系统把Window句柄分配给某个线程。
~ 进程之间可以并发执行:如你可以运行多个记事本程序,运行多个QQ。

2.线程

~线程有一个优先级、实际上正在处理的程序的位置记数器、一个存储其局部变量的栈。每个线程都有自己的栈,但程序代码和堆由一个进程的所有线程共享。由于该进程的所有线程都寻址相同的虚拟地址,这使得一个进程所有的线程之间通信非常快。
~线程之间也可以并发执行:如你运行Word程序时,一个线程等待用户输入,一个线程进行拼写检查,一个线程将数据存入临时文件中…
~ 比如媒体播放器播放一个电影中同时放音乐也同时播放画面,如果没有多线程,那么操作系统必须等到音乐播完或者画面放完才能进行下一个操作。

简而言之:进程是一个容器,线程在进程这个容器中执行,是实际执行的代码。一
个进程至少拥有一个线程,而这个线程通常被称之为主线程(执行Main()方法的
线程通常被称为主线程),主线程的入口点也是应用程序的起始点。线程除了必不
可少的资源(程序计数器 一组寄存器和栈)之外,不拥有系统资源,所有进程内
的线程共享分配给这个进程拥有的所有资源。

3.句柄

句柄实际上是一个数据,是一个Long (整长型)的数据。
句柄是一种指向指针的指针
本质:WINDOWS程序中并不是用物理地址来标识一个内存块,文件,任务或动态装入模块的,相反的,WINDOWS API给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。

三、DLL生成及调用

  1. 生成
// yz.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#ifdef __cplusplus         // if used by C++ code
extern "C" {                  // we need to export the C interface
#endif

__declspec(dllexport) int addfun(int a, int b)//定义导出函数
{
        return a+b;
}

#ifdef __cplusplus
}
#endif
// user.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
using namespace std;
#define DEF_DLL_NAME "yz.dll"
typedef int(*FUNA)(int,int);
void main()
{

    HMODULE hMod = LoadLibraryA("yz.dll");//dll路径
    if (hMod)
    {
        FUNA addfun = (FUNA)GetProcAddress(hMod, ("addfun"));//直接使用原工程函数名 
        if (addfun != NULL)
        {
            cout<<addfun(5, 4)<<endl;
        }
        else
        {
            cout<<"ERROR on GetProcAddress"<<endl;
        }
        FreeLibrary(hMod);
    }
    else
        cout<<"ERROR on LoadLibrary"<<endl;
    while(1);
}

四、HOOK钩取

钩子
是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

实现方法:SetWindowsHookEx方式注入
目的:将DLL注入到其他进程中并执行恶意代码
具体步骤:

假设A.exe要将 val.dll注入到B.exe,主要的流程如下:
①编写val.dll,其中val.dll必须至少导出一个函数,因为后面要传给SetWindowsHookEx作为回调函数。
②A加载val.dll,获取回调函数地址f.
③调用SetWindowsHookEx设置全局钩子,钩子的类型由自己决定。学习中使用的是WH_KEYBOARD。传入回调函数地址,dll的句柄,线程ID,设置为0表示对全局有效。如果函数返回非NULL,则说明设置成功。
④B运行起来后,如果触发了和钩子对应的消息,则会去运行f,但f在val.dll中,并不在进程空间里,因此B会去加载val.dll。这样,dll就被注入到目标进程中了。

SetWindowsHookEx只会负责把你的DLL加载到其他的执行程序(不会执行),所以我们的替换动作必须在DLLMAIN函数中(或调用)完成
当SetWindowsHookEx最后一个参数为0时,代表全局钩子。监视全部进程。

工程代码:

//HookMain.cpp
#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();

    // 循环
    printf("press 'q' to quit!\n");
    while( _getch() != 'q' )    ;

    // 结束
    HookStop();

    //释放
    FreeLibrary(hDll);
}
//KeyHook.cpp
#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[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, '\\');
            if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
                return 1;
        }
    }

    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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值