代码之家  ›  专栏  ›  技术社区  ›  Bruno Fauth

为什么系统调用“process\u vm\u readv”会将errno设置为“success”?

  •  0
  • Bruno Fauth  · 技术社区  · 8 年前

    我正在尝试用Python 3实现一个调试器。其主要思想非常简单:包装系统调用“ process_vm_readv “然后在其他进程上调用它。

    我还创建了一个小型的虚拟C++程序,以便使用此工具进行调试。以下是他们的来源:


    调试器

    #!/usr/bin/python3
    
    import typing
    import ctypes
    import os
    
    libc = ctypes.cdll.LoadLibrary("libc.so.6")
    
    def _error_checker(result, function, arguments):
        if result == -1:
            errno = ctypes.get_errno()
            raise OSError(errno, os.strerror(errno))
    
    class IOBuffer(ctypes.Structure): # iovec struct
        _fields_ = [("base", ctypes.c_void_p),
                    ("size", ctypes.c_size_t)]
    
    _read_process_memory = libc.process_vm_readv
    _read_process_memory.restype = ctypes.c_ssize_t
    _read_process_memory.errcheck = _error_checker
    _read_process_memory.args = [ctypes.c_ulong, ctypes.POINTER(IOBuffer),
                                ctypes.c_ulong, ctypes.POINTER(IOBuffer),
                                ctypes.c_ulong, ctypes.c_ulong]
    
    def read_process_memory(pid: int, base: int, size: int) -> typing.Tuple[int, bytes]:
        buffer = (ctypes.c_char * size)()
        local = IOBuffer(ctypes.addressof(buffer), size)
        remote = IOBuffer(base, size)
        return _read_process_memory(pid, local, 1, remote, 1, 0), buffer.raw
    

    虚拟程序

    #include <iostream>
    #include <stdio.h>
    
    using namespace std;
    
    int main(void){
        int a = 99;
        int c;
        while((c = getchar()) != EOF)
            cout << "int a=" << a << ";\t&a=" << &a << endl;
        return 0;
    }
    

    我的问题在于,每当我用虚拟程序的pid调用“read\u process\u memory”时,它提供给我的内存地址和数字4( int )作为参数(应该可以工作),包装的系统调用返回-1(错误)。当这种情况发生时, errcheck 报告该操作的错误号,其结果总是为零。“错误成功”。由于此错误消息无效,我不知道如何解决此问题。你们对如何解决这个问题有什么想法吗?

    2 回复  |  直到 8 年前
        1
  •  1
  •   Employed Russian    8 年前

    由于此错误消息无效,我不知道如何解决此问题

    你可以 总是 找出 真实的 内核返回的错误 strace . 像这样的方法应该会奏效:

    strace -e process_vm_readv python test.py
    
        2
  •  1
  •   Bruno Fauth    8 年前

    根据建议 Employed Russian's answer ,我用运行调试工具 strace -e process_vm_readv 在命令行中前置。它给了我以下错误:

    process_vm_readv(3464,
                     [{iov_base=NULL, iov_len=0},
                      {iov_base=NULL, iov_len=0},
                      {iov_base=0x7f9d39c0c4f8, iov_len=140313255527400},
                      {iov_base=0xfffffffffffffffa, iov_len=4}],
                     4, 0x1, 140726046508276, 4)
    = -1 EINVAL (Invalid argument)
    

    在对代码进行了一段时间的修改后,我通过更改 read_process_memory 功能到:

    def read_process_memory(pid: int, base: int, size: int) -> typing.Tuple[int, bytes]:
        buffer = (ctypes.c_char * size)()
        local = (IOBuffer * 1)()
        local[0].base, local[0].size = ctypes.addressof(buffer), size
        remote = (IOBuffer * 1)()
        remote[0].base, remote[0].size = base, size
        return _read_process_memory(pid, local, 1, remote, 1, 0), buffer.raw
    

    我现在唯一的问题是strace向我抛出了以下错误:

    process_vm_readv(3464,
                     [{iov_base=0x7fedc6f64450, iov_len=4}], 1,
                     [{iov_base=0x7ffd560344f4, iov_len=4}], 1, 0)
    = -1 EPERM (Operation not permitted)
    

    对此,我以root身份运行所有这些,解决了最后一个问题。


    编辑: 正如Mark Tolonen所建议的,不是在 read\u process\u内存 ,这两个结构都可以通过 ctypes.byref 具体如下:

    def read_process_memory(pid: int, base: int, size: int) -> typing.Tuple[int, bytes]:
        buffer = (ctypes.c_char * size)()
        local = IOBuffer(ctypes.addressof(buffer), size)
        remote = IOBuffer(base, size)
        return _read_process_memory(pid, ctypes.byref(local), 1, ctypes.byref(remote), 1, 0), buffer.raw