为什么系统调用 "process_vm_readv" 将 errno 设置为 "success"?
Why does the syscall "process_vm_readv" sets errno to "success"?
我正在尝试在 Python 中实现调试器 3. 主要思想非常简单:用 ctypes 包装系统调用“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_process_memory" 时,它提供给我的内存地址和数字 4(int
的大小)作为参数 -哪个应该工作 - 包装的系统调用 returns -1 (错误)。当发生这种情况时,errcheck
报告该操作的 errno,它总是最终为零。 "Error Success"。由于这个无用的错误消息,我不知道如何解决这个问题。你们对如何解决这个问题有什么想法吗?
Due to this unhelpful error message I don't know how to fix this issue
您可以总是找出内核返回的真实错误strace。这样的事情应该有效:
strace -e process_vm_readv python test.py
根据 的建议,我 运行 我的调试工具在命令行中添加了 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_process_memory
中声明两个大小为 1 的数组,而是可以使用 [=] 将两个结构传递给包装函数17=]如下:
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
我正在尝试在 Python 中实现调试器 3. 主要思想非常简单:用 ctypes 包装系统调用“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_process_memory" 时,它提供给我的内存地址和数字 4(int
的大小)作为参数 -哪个应该工作 - 包装的系统调用 returns -1 (错误)。当发生这种情况时,errcheck
报告该操作的 errno,它总是最终为零。 "Error Success"。由于这个无用的错误消息,我不知道如何解决这个问题。你们对如何解决这个问题有什么想法吗?
Due to this unhelpful error message I don't know how to fix this issue
您可以总是找出内核返回的真实错误strace。这样的事情应该有效:
strace -e process_vm_readv python test.py
根据 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_process_memory
中声明两个大小为 1 的数组,而是可以使用 [=] 将两个结构传递给包装函数17=]如下:
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