Python Linux读取进程内存 - int太大而无法转换为C long

时间:2013-05-12 10:11:04

标签: python linux memory python-2.7

我有这个片段在Linux中读取进程内存并搜索字符串,在某些发行版上运行正常,但在其他发行版上只有这个错误:

  maps_file = open("/proc/%s/maps"%pid, 'r')
  mem_file = open("/proc/%s/mem"%pid, 'r')
  for line in maps_file.readlines():  # for each mapped region
      m = re.match(r'([0-9A-Fa-f]+)', line)
      if m.group(3) == 'r':  # if this is a readable region
          start = int(m.group(1), 16)
          end = int(m.group(2), 16)
          mem_file.seek(start)  # seek to region start
          chunk = mem_file.read(end - start)  # read region contents
          #print chunk,  # dump contents to standard output
          mem_dump = open(working_dir+"/%s.bin"%pid, "ab")
          mem_dump.write(chunk,)
          mem_dump.close()
  maps_file.close()
  mem_file.close()

错误:

scan process: 491
Traceback (most recent call last):
  File "./dump.py", line 106, in <module>
    MainDump(pid)
  File "./dump.py", line 79, in MainDump
    mem_file.seek(start)  # seek to region start
OverflowError: Python int too large to convert to C long

问题在于:

start = int(m.group(1), 16)

mem_file.seek(start)

我应该声明为float吗?有什么想法吗?

还尝试了long()相同的结果和错误。

编辑:我忘了说的是我在“x64”系统上遇到的错误。

1 个答案:

答案 0 :(得分:2)

问题是你有地址0xffffffffff600000L。 (签名)C long只能保存从-0x80000000000000000x7fffffffffffffff的值。所以,这个地址确实“太大而无法转换为C long”。

如果你看一下the source,你会发现问题最有可能是因为某些原因,当在非工作发行版上配置Python时,它无法检测到fseekooff_t存在。但除非您想重建Python,否则无法帮助您。

那么,你怎么解决这个问题呢?有几件事要尝试。


第一种可能性是从头而不是开始寻求。

mem_len = os.fstat(mem_file.fileno()).st_size

if start >= 1<<63L:
    mem_file.seek(mem_len - start, os.SEEK_END)
else:
    mem_file.seek(start)

你也可以试试这个可怕的黑客:

if start >= 1<<63L:
    start -= 1<<64L

这会将您的0xffffffffff600000L转换为-0xa00000,这恰好适用于long ...然后希望long实际上被转换为某些无符号的64-在C层内部的位类型,意味着它按照您的希望寻找0xffffffffff600000L


您也可以使用mmap映射所需的网页,而不是seekread来解决此问题。


如果最糟糕的情况发生,您可以使用ctypes(或cffi或您喜欢的任何内容)直接在文件句柄上调用fseeko


最后,请确保您确实想要阅读此区域。我可能错了,但我似乎记得linux保留了映射到用户空间的内核页面的上部区域。如果我是对的,你正在寻找的字符串不会在这里,所以你可以跳过它们......

要跳过处理区域,您可以在if:

中移动处理
start = int(m.group(1), 16)
end = int(m.group(2), 16)
if start <= sys.maxint:
    mem_file.seek(start)  # seek to region start
    chunk = mem_file.read(end - start)  # read region contents
    # ...

...或使用continue语句跳到循环的下一次迭代:

start = int(m.group(1), 16)
end = int(m.group(2), 16)
if start > sys.maxint:
    continue
mem_file.seek(start)  # seek to region start
chunk = mem_file.read(end - start)  # read region contents
# ...

如果您知道区域始终按排序顺序排列,则可以使用break代替continue(因为其余区域也将超出范围)。

但我认为最好的解决方案就是try它,并处理错误。此seekread可能会失败的其他原因 - 例如,如果您正在查看的流程在您到达之前取消映射某个区域,或者退出 - 而您宁愿跳过该错误并继续,而不仅仅是退出,对吗?

所以:

if m.group(3) == 'r':  # if this is a readable region
    start = int(m.group(1), 16)
    end = int(m.group(2), 16)
    try:
        mem_file.seek(start)  # seek to region start
        chunk = mem_file.read(end - start)  # read region co
    except Exception as e:
        print('Skipping region {:#018x} because of error {}'.format(start, e))
        continue
    mem_dump = open(working_dir+"/%s.bin"%pid, "ab")
    # ...