如何编写Perl,Python或Ruby程序来改变Windows上另一个进程的内存?

我想知道是否可以使用Perl,Python或Ruby来编写程序,以便它在另一个进程的内存中查找0x12345678(可能是堆,对于数据和代码数据)然后如果找到它,则更改它到0x00000000? 它类似于Cheat Engine ,可以在Windows上执行类似的操作。

我最初认为这是不可能的,但在看到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 

如果您已将程序作为调试程序附加到进程,则可以这样做,如果存在适当API的包装,则应该可以使用这些语言,或者通过类似ctypes(用于python)直接访问Windows函数。 但是,使用更低级别的语言可能更容易,因为在更高级别的语言中,您必须关注如何将高级数据类型转换为较低级别的语言。

首先调用要调试的进程上的OpenProcess ,并请求相应的访问权限(您需要成为计算机上的管理员/具有相当高的权限才能获得访问权限)。 然后,您应该能够调用ReadProcessMemory和WriteProcessMemory等函数来读取和写入该进程的内存。

[编辑]这是一个快速python概念的function概念,成功从另一个进程的地址空间读取内存:

 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等内容。

好吧,有趣的部分是访问其他进程的内存。 CheatEngine通过在虚拟机下运行整个操作系统来实现它,从而使内存保护失效。 还有“在调试器下运行”模型,通常意味着将目标应用程序作为修改应用程序的子进程启动,并具有提升的权限。 有关这方面的许多有趣的东西,请参阅Win32 API 。

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

使用Process injection,延迟加载库等方法可以做到这一点。

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

“随着力量的增加……”

祝好运

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

基于Brian的正确答案,这是一个使用dll从python中获取地址的快速而肮脏的例子。 当然,这是特定于DLL的实现。 “模块名称”通常是Cheat Engines“Enumerate DLLs and Symbols”对话框中显示的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) 

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

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