1# Copyright (c) 2014, Tomohiro Kusumi 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# 1. Redistributions of source code must retain the above copyright notice, this 8# list of conditions and the following disclaimer. 9# 2. Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# 13# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 24import os 25 26from . import filebytes 27from . import fileobj 28from . import kernel 29from . import log 30from . import ptrace 31from . import setting 32from . import util 33 34enabled = setting.use_pid_path and ptrace.has_ptrace() 35 36class methods (object): 37 def get_string(self): 38 l = [] 39 l.append("pid {0}".format(self.pid)) 40 l.append("name " + self.name) 41 l.append("word size {0}".format(self.word)) 42 return '\n'.join(l) 43 44 def init_vm(self): 45 self.word = ptrace.get_word_size() 46 assert self.word != -1 47 self.pid = kernel.path_to_pid(self.get_path()) 48 self.name = kernel.get_pid_name(self.pid) 49 self.test_vm() 50 51 offset = self.get_mapping_offset() 52 length = self.get_mapping_length() 53 if not length: 54 length = util.get_address_space() # will fail 55 length -= offset 56 self.init_chunk(self.__load_buffer(offset, length)) 57 58 def __load_buffer(self, offset, length): 59 beg, end = util.align_range(offset, offset + length, self.word) 60 buf = self.read_vm(beg, end - beg) 61 x = offset - beg 62 b = buf[x : x + length] 63 assert len(b) == length, len(b) 64 return b 65 66 def get_vm_alias(self): 67 return self.name 68 69 def get_address(self, x): 70 return self.get_mapping_offset() + x 71 72 def __wait(self): 73 pid, status = kernel.waitpid(self.pid, 0) 74 if setting.use_debug: 75 ret = kernel.parse_waitpid_result(status) 76 log.debug("Wait pid {0}: {1}".format(pid, ret)) 77 78 def test_vm(self): 79 if not kernel.has_pid_access(self.pid): 80 raise fileobj.Error("Can not access pid {0}".format(self.pid)) 81 82 def __attach_vm(self): 83 ret, err = ptrace.attach(self.pid) 84 if ret == ptrace.ERROR: 85 raise fileobj.Error("Failed to attach pid {0}: {1}".format(self.pid, 86 os.strerror(err))) 87 self.__wait() 88 89 def __detach_vm(self): 90 ret, err = ptrace.detach(self.pid) 91 if ret == ptrace.ERROR: 92 raise fileobj.Error("Failed to detach pid {0}: {1}".format(self.pid, 93 os.strerror(err))) 94 95 def __assert_vm(self, addr, size): 96 assert addr % self.word == 0, addr 97 assert size % self.word == 0, size 98 99 def read_vm(self, addr, size): 100 self.__assert_vm(addr, size) 101 self.__attach_vm() 102 try: 103 return self.__peek_vm(addr, size) 104 except Exception as e: 105 log.error("{0}, retrying".format(e)) 106 return self.__peek_vm(addr, size) 107 finally: 108 self.__detach_vm() 109 110 def __peek_vm(self, addr, size): 111 l = [] 112 while True: 113 ret, err = ptrace.peektext(self.pid, addr) 114 if ret == ptrace.ERROR: 115 raise fileobj.Error( 116 "Failed to peek pid {0} at 0x{1:X}: {2}".format(self.pid, 117 addr, os.strerror(err))) 118 l.append(ret) 119 addr += self.word 120 size -= self.word 121 if size <= 0: 122 break 123 buf = filebytes.join(l) 124 assert len(buf) % self.word == 0, len(buf) 125 return buf 126 127 def write_vm(self, addr, buf): 128 self.__assert_vm(addr, 0) 129 self.__attach_vm() 130 try: 131 return self.__poke_vm(addr, buf) 132 except Exception as e: 133 log.error("{0}, retrying".format(e)) 134 return self.__poke_vm(addr, buf) 135 finally: 136 self.__detach_vm() 137 138 def __poke_vm(self, addr, buf): 139 ret = 0 140 for data in buf: 141 ret, err = ptrace.poketext(self.pid, addr, data) 142 if ret == ptrace.ERROR: 143 raise fileobj.Error( 144 "Failed to poke pid {0} at 0x{1:X}: {2}".format(self.pid, 145 addr, os.strerror(err))) 146 ret += 1 147 addr += self.word 148 return ret 149