在Ruby中,如何从外部进程读取内存值?

3
我想要做的就是编写一个Ruby程序,从另一个进程的虚拟内存中读取一些已知内存地址的值。通过我的研究和对运行进程的x86汇编在内存中的十六进制编辑的基本知识,我已经找到了我想要的内存中值的基址和偏移量。我不想改变它们,我只想读取它们。我向一个内存编辑器的开发者询问了如何处理这种语言的抽象问题,并假设使用Windows平台。他告诉我,使用C或C++的Win32API调用OpenProcess、CreateProcess、ReadProcessMemory和WriteProcessMemory是正确的方法。我认为最好的方法是只使用Win32API类并映射两个实例;一个用于OpenProcess或CreateProcess,具体取决于用户是否已经运行该进程,另一个实例将被映射到ReadProcessMemory。我可能还需要找到获取正在运行的进程列表的函数,以便知道如果它已经在运行,则想要的运行进程是哪个。

这需要花费一些时间来组合所有内容,但我认为编码起来不会太难。这对我来说只是一个新的编程领域,因为我从未从高级语言(至少比C更高级)的低级别工作过。我只是想知道处理这个问题的方法。我可以只使用一堆Win32API调用,但这意味着必须处理一堆与系统相关的字符串和数组打包和解包,我希望最终使其跨平台工作,因为我正在读取的进程是由具有多个平台构建的可执行文件生成的(我知道内存地址在不同的系统上会发生变化。想法是有一个扁平的文件,其中包含所有内存映射,以便Ruby程序可以将当前平台环境与匹配的内存映射进行匹配。)但从事情的外观来看,我只能制作一个类,包装任何当前平台的系统共享库内存相关的函数调用。

我不知道是否已经存在一个Ruby gem可以为我处理所有这些问题,我可能只是没有找到。我还可以尝试编辑每个构建的可执行文件,使得无论何时我想要读取的内存值被进程写入时,它也会将新值的副本写入共享内存中的空间,我以某种方式让Ruby创建一个指向该共享内存地址的指针类的实例,并向Ruby程序发出信号,表示该值已更新并应重新加载。基本上,基于中断的系统很好,但由于读取这些值的目的只是将其发送到从中央服务器广播的记分板,因此我可以坚持基于轮询的系统,在固定时间间隔内发送更新。我也可以完全放弃Ruby,转而使用C或C++,但我不太了解它们。实际上,我比C++更了解x86,我只知道与系统无关的ANSI C,并且从未处理过共享系统库。

是否有可用的宝石或较少人知道的模块已经完成了这个?如果没有,那么任何关于如何完成此操作的其他信息都将是不错的。我想,简而言之,我该怎么做呢?

提前感谢, Grg

PS:确认那些Win32API调用应该针对kernel32.dll库会很好。

2个回答

2

使用 Ruby-FFI:

如果您了解 C 和 Win32 API,那么您可能会发现使用 Ruby-FFI gem 更容易。 Ruby-FFI 可以透明地封装 C 函数,从而允许您使用任何 Win32 函数。

对于命名的共享内存,Win32 提供了 CreateFileMapping()MapViewOfFile()

这些函数的头文件为:

# WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE,LPSECURITY_ATTRIBUTES,DWORD,DWORD,DWORD,LPCSTR);
# WINBASEAPI PVOID WINAPI MapViewOfFile(HANDLE,DWORD,DWORD,DWORD,DWORD);

通过将无效的文件句柄0xFFFFFFFF传递给CreateFileMapping,您可以避免处理文件,并使用数组和指针定义共享内存。
下面是一个简化的示例,它从共享内存中读取。(生产质量版本将在读者和写入者中使用信号量和循环缓冲区,以允许两个进程异步进行,同时仍然确保写入者不会覆盖缓冲区,直到读者完成读取。)
简化版Ruby示例:
require 'ffi'

module Win32
   extend FFI::Library
   ffi_lib 'kernel32'  # see winbase.h
   attach_function :CreateFileMapping, 
            :CreateFileMappingA,[ :uint, :pointer, :long, :long, :long, :pointer ], :pointer   
            # suffix A indicates the ASCII version
   attach_function :MapViewOfFile, 
            :MapViewOfFile,[ :pointer, :long, :long, :long, :long ], :pointer  
end

memoryName = "Share Memory Name"
sz_buf = 1000  # bytes  (250 ints, 4 bytes each)
num_ints = sz_buf / 4

# Windows constants
PAGE_READWRITE = 0x0004
FILE_MAP_WRITE = 2

# Get handle to shared memory
hMemory = Win32.CreateFileMapping(0xFFFFFFFF, nil, PAGE_READWRITE, 0, sz_buf, memoryName)

# Create pointer into shared memory in the reader's memory space 
pMemory = FFI::MemoryPointer.new(:int, num_ints )
pMemory = Win32.MapViewOfFile(hMemory, FILE_MAP_WRITE, 0, 0, sz_buf)

# Read from shared memory buffer
puts pMemory.read_array_of_int(sz_buf).join(" ")  

一些有用的信息:


2

看看win32utils。我认为这至少可以让您站起来,并为您提供如何深入挖掘API本身的示例,如果宝石本身对您不起作用。您可能需要下定决心编写一个C模块。


是的,我之前看过了。按照他们组织win32utils模块的方式,我要找的内容应该在win32-process子模块下,并且它确实包含了与我需要的win32-api中的进程函数相同的函数集,但是win32utils的开发者尚未实现ReadProcessMemory和/或WriteProcessMemory函数。 - grg-n-sox
抱歉出现了重复的评论,但我想知道为什么要创建一个 C 模块,而不是只创建几个与不同函数调用相关联的 Win32API 类实例,并将它们封装在 Ruby 类中,我可以称之为 ExternalProcessMemory? - grg-n-sox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接