如何编写Perl、Python或Ruby程序在Windows中更改其他进程的内存?

8 投票
6 回答
4667 浏览
提问于 2025-04-15 12:20

我在想,Perl、Python 或 Ruby 这些编程语言能不能用来写一个程序,让它去查找另一个进程的内存中是否有 0x12345678 这个值(可能是在堆内存里,既包括数据也包括代码数据),如果找到了,就把它改成 0x00000000?这有点像 Cheat Engine,它在 Windows 上可以做到类似的事情。

6 个回答

6

有趣的部分在于如何访问其他进程的内存。CheatEngine 通过在虚拟机中运行整个操作系统来实现,这样就可以绕过内存保护。还有一种方法是“在调试器下运行”,这通常意味着将目标应用程序作为修改应用程序的子进程启动,并且拥有更高的权限。想了解更多有趣的内容,可以查看Win32 API

在 Perl 中,一旦你获得了必要的访问权限,你可能会想使用Win32::Security::Raw来与之交互。

8

如果你把你的程序当作调试工具连接到某个进程上,这是可以做到的。在一些编程语言中,如果有合适的API封装,或者像Python中的ctypes这样的工具,可以直接访问Windows的功能,就能实现这个。不过,使用一些底层语言可能会更简单,因为在高级语言中,你需要考虑如何将高级数据类型转换为低级数据类型等等。

首先,你需要调用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等。

13

我最开始以为这不可能,但在看到Brian的评论后,我在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

撰写回答