【问题标题】:How to write a Perl, Python, or Ruby program to change the memory of another process on Windows?如何编写 Perl、Python 或 Ruby 程序来更改 Windows 上另一个进程的内存?
【发布时间】:2009-06-18 16:45:46
【问题描述】:

我想知道是否可以使用 Perl、Python 或 Ruby 编写程序,以便它在另一个进程的内存(可能是堆,用于数据和代码数据)中查找 0x12345678,然后是否找到,改成0x00000000?它类似于Cheat Engine,可以在Windows 上做类似的事情。

【问题讨论】:

    标签: python ruby perl winapi readprocessmemory


    【解决方案1】:

    我最初认为这是不可能的,但在看到布赖恩的评论后,我搜索了 CPAN,你瞧,有Win32::Process::Memory

    C:\> ppm install Win32::Process::Info
    C:\> ppm install Win32::Process::Memory
    

    该模块显然使用了ReadProcessMemory 函数:这是我的尝试之一:

    #!/usr/bin/perl
    use strict; use warnings;
    
    use Win32;
    use Win32::Process;
    use Win32::Process::Memory;
    
    my $process;
    
    Win32::Process::Create(
        $process,
        'C:/opt/vim/vim72/gvim.exe',
        q{},
        0,
        NORMAL_PRIORITY_CLASS,
        q{.}
    ) or die ErrorReport();
    
    my $mem = Win32::Process::Memory->new({
        pid => $process->GetProcessID(),
        access => 'read/query',
    });
    
    $mem->search_sub( 'VIM', sub {
        print $mem->hexdump($_[0], 0x20), "\n";
    });
    
    sub ErrorReport{
        Win32::FormatMessage( Win32::GetLastError() );
    }
    
    END { $process->Kill(0) if $process }
    

    输出:

    C:\Temp> proc
    0052A580 : 56 49 4D 20 2D 20 56 69 20 49 4D 70 72 6F 76 65 : VIM - Vi IMprove
    0052A590 : 64 20 37 2E 32 20 28 32 30 30 38 20 41 75 67 20 : d 7.2 (2008 Aug
    
    0052A5F0 :       56 49 4D 52 55 4E 54 49 4D 45 3A 20 22 00 :   VIMRUNTIME: ".
    0052A600 : 20 20 66 61 6C 6C 2D 62 61 63 6B 20 66 6F 72 20 :   fall-back for
    0052A610 : 24 56                                           : $V
    

    【讨论】:

    • 这不是真的 - 通过适当的权限,可以访问和修改另一个进程的内存(这是调试器的工作方式)如果您已经是管理员,这不是安全漏洞。
    【解决方案2】:

    如果您已将程序作为调试器附加到进程,则可以这样做,如果存在围绕适当 API 的包装器,或者通过 ctypes 之类的东西直接访问 Windows 函数(例如Python)。但是,使用更低级的语言可能更容易,因为在更高级别的语言中,您必须关心如何将高级数据类型转换为低级数据类型等。

    首先在进程上调用OpenProcess 进行调试,并请求适当的访问权限(您需要成为机器上的管理员/拥有相当高的权限才能获得访问权限)。然后,您应该能够调用 ReadProcessMemoryWriteProcessMemory 之类的函数来读取和写入该进程的内存。

    [编辑]这是一个快速的 Python 概念证明,该函数成功地从另一个进程的地址空间读取内存:

    import ctypes
    import ctypes.wintypes
    kernel32 = ctypes.wintypes.windll.kernel32
    
    # Various access flag definitions:
    class Access:
        DELETE      = 0x00010000
        READ_CONTROL= 0x00020000
        SYNCHRONIZE = 0x00100000
        WRITE_DAC   = 0x00040000
        WRITE_OWNER = 0x00080000
        PROCESS_VM_WRITE = 0x0020
        PROCESS_VM_READ = 0x0010
        PROCESS_VM_OPERATION = 0x0008
        PROCESS_TERMINATE = 0x0001
        PROCESS_SUSPEND_RESUME = 0x0800
        PROCESS_SET_QUOTA = 0x0100
        PROCESS_SET_INFORMATION = 0x0200
        PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
        PROCESS_QUERY_INFORMATION = 0x0400
        PROCESS_DUP_HANDLE = 0x0040
        PROCESS_CREATE_THREAD = 0x0002
        PROCESS_CREATE_PROCESS = 0x0080
    
    def read_process_mem(pid, address, size):
        """Read memory of the specified process ID."""
        buf = ctypes.create_string_buffer(size)
        gotBytes = ctypes.c_ulong(0)
        h = kernel32.OpenProcess(Access.PROCESS_VM_READ, False, pid)
        try:
            if kernel32.ReadProcessMemory(h, address, buf, size, ctypes.byref(gotBytes)):
                return buf
            else:
                # TODO: report appropriate error GetLastError
                raise Exception("Failed to access process memory.")
        finally:
            kernel32.CloseHandle(h)
    

    请注意,您需要确定在内存中的何处查找内容 - 大多数地址空间都将被取消映射,认为有一些标准偏移量可以查找程序代码、dll 等内容。

    【讨论】:

      【解决方案3】:

      嗯,有趣的部分是访问其他进程的内存。 CheatEngine 通过在允许内存保护失效的虚拟机下运行整个操作系统来做到这一点。还有“在调试器下运行”模型,通常意味着将目标应用程序作为修改应用程序的子进程启动,并具有提升的权限。请参阅 Win32 API 了解很多有趣的内容。

      在 Perl 中,一旦您获得了必要的访问权限,您可能希望使用 Win32::Security::Raw 与它进行交互。

      【讨论】:

      • 这是否意味着如果我运行作弊引擎以及第三方应用程序或共享软件,计算机特别容易受到攻击,因为整个计算机都关闭了内存保护? (共享软件本质上可以修改IE或Firefox进程的代码或数据)
      • 不,没有那么糟糕。 CheatEngine 通过扩展的操作集开展业务,并使用散列密钥验证对其的访问。因此,它不仅仅是简单地关闭内存保护,它还允许通过至少有一些保护的机制绕过它。
      【解决方案4】:

      有一些方法可以使用进程注入、延迟加载库等来做到这一点。

      我没有看到您使用您列出的工具执行此操作。这是 C 和汇编语言的国家,开始让你进入病毒编写领域。一旦你让它工作,任何反病毒包都会否决它运行并尝试隔离它。所以你最好真的想要这样做。

      “权力越大……”

      祝你好运

      【讨论】:

        【解决方案5】:

        可以使用列出的一种语言来实现整个过程,但编译语言更适合内存扫描(如果没有其他考虑,则考虑速度)。有一个名为 SigScan 的 dll(带有源代码)可用,虽然它是为特定游戏量身定制的,但可以通过最小的努力进行修改以满足您的需求。

        在 Brian 的正确答案的基础上,这里有一个使用 dll 从 python 中获取地址的快速而肮脏的示例。 当然,这是特定于 DLL 实现的。 “模块名称”通常是作弊引擎“枚举 DLL 和符号”对话框中显示的 dll 名称。

        以 Brian 的示例为指导和 MSDN,您可以使用自己的 WriteProcessMemory 方法轻松扩展它。

        import win32defines
        import win32process
        import win32gui
        from ctypes import *
        SigScan = cdll.SigScan
        kernel32 = windll.kernel32
        addresses = {"Value1" : {"sigArg1" : "b0015ec390518b4c24088d4424005068", 
                                  "sigArg2" : 36, 
                                  "address" : None,
                                  "size"    : 32
                                 },
                    "Value2" :{"sigArg1" : "3b05XXXXXXXX741285c0",
                                  "sigArg2" : None, 
                                  "address" : None,
                                  "size"    : 32
                                }
                }
        
        def read_process_mem(pid, address, size):
            """Read memory of the specified process ID."""
            buf = create_string_buffer(size)
            gotBytes = c_ulong(0)
            h = kernel32.OpenProcess(win32defines.PROCESS_VM_READ, False, pid)
            try:
                if kernel32.ReadProcessMemory(h, address, buf, size, byref(gotBytes)):
                    return buf
                else:
                    # TODO: report appropriate error GetLastError
                    raise Exception("Failed to access process memory.")
            finally:
                kernel32.CloseHandle(h)
        if __name__ == "__main__":
            pid, id = None, None
            ## HWND 
            hwnd = win32gui.FindWindowEx(0, 0, 0, "Window Name here")
            ## pid
            pid = win32process.GetWindowThreadProcessId(hwnd)[-1]
            ## Initialize the sigscan dll
            SigScan.InitializeSigScan(pid, "Module Name")
            ## Find all the addresses registered
            for key in addresses.keys():
                addresses[key]["address"] = SigScan.SigScan(addresses[key]["sigArg1"],
                    addresses[key]["sigArg2"])
            ## Allow the scanner to clean up
            SigScan.FinalizeSigScan()
            for key in addresses.keys():
                if addresses[key]["address"] != None:
                    print repr(read_process_mem(pid, addresses[key]["address"],
                                    addresses[key]["size"]).raw)

        【讨论】:

          【解决方案6】:

          为此,我编写了Proc::Memory 及其底层库libvas。它只是在 Windows 的后台调用{Read,Write}ProcessMemory,但它也支持其他平台。示例:

          my $mem = Proc::Memory->new(pid => $$); 
          $mem->poke(0x12345678, 'L') = 12;
          

          【讨论】:

            猜你喜欢
            • 2011-06-05
            • 1970-01-01
            • 2011-07-24
            • 1970-01-01
            • 2021-06-09
            • 2013-01-05
            • 2010-10-31
            • 2010-12-12
            相关资源
            最近更新 更多