def find_function_symbol(self, task, address): """ Match a function symbol to a functiona address. @param task: the task_struct @param address: The function address @return: The function symbol or None """ if self.symbols: for vma in task.get_proc_maps(): if vma.vm_start <= address <= vma.vm_end: #lib = vma.vm_file lib = linux_common.get_path(task, vma.vm_file) offset = address - vma.vm_start #libsymbols = self.symbols[os.path.basename(lib)] if type(lib) == list: lib = "" base = os.path.basename(lib) #print(base) #print("{:016x} {} {}".format(offset, base, lib)) if base in self.symbols: if offset in self.symbols[base]: debug.info("Instruction was a call to 0x{:016x} = {}@{}".format(address, self.symbols[base][offset], base )) return self.symbols[base][offset] elif address in self.symbols[base]:# for a function in the main binary, eg 0x40081e debug.info("Instruction was a call to 0x{:016x} = {}@{}".format(address, self.symbols[base][address], base )) return self.symbols[base][address] break return None
def _fill_cache(self): for task in linux_pslist.linux_pslist(self._config).calculate(): for filp, fd in task.lsof(): filepath = linux_common.get_path(task, filp) if type(filepath) == str and filepath.find("socket:[") != -1: to_add = filp.dentry.d_inode.i_ino.v() self.fd_cache[to_add] = [task, filp, fd, filepath]
def render_text(self, outfd, data): for task, vma in data: mm = task.mm if vma.vm_file: inode = vma.vm_file.dentry.d_inode sb = obj.Object("super_block", offset = inode.i_sb, vm = self.addr_space) dev = sb.s_dev ino = inode.i_ino pgoff = vma.vm_pgoff << 12 fname = linux_common.get_path(task, vma.vm_file) else: (dev, ino, pgoff) = [0] * 3 if vma.vm_start <= mm.start_brk and vma.vm_end >= mm.brk: fname = "[heap]" elif vma.vm_start <= mm.start_stack and vma.vm_end >= mm.start_stack: fname = "[stack]" else: fname = "" outfd.write("{0:#8x}-{1:#8x} {2:3} {3:10d} {4:#2d}:{5:#2d} {6:#12d} {7}\n".format( self.mask_number(vma.vm_start), self.mask_number(vma.vm_end), self.format_perms(vma.vm_flags), pgoff, self.MAJOR(dev), self.MINOR(dev), ino, fname))
def _fill_cache(self): for task in linux_pslist.linux_pslist(self._config).calculate(): for filp, fd in task.lsof(): filepath = linux_common.get_path(task, filp) if type(filepath) == str and filepath.find("socket:[") != -1: to_add = filp.dentry.d_inode.i_ino self.fd_cache[to_add] = [task, filp, fd, filepath]
def render_text(self, outfd, data): self.table_header(outfd, [("Pid", "8"), ("FD", "8"), ("Path", "")]) for (task, filp, fd) in data: self.table_row(outfd, task.pid, fd, linux_common.get_path(task, filp))
def render_text(self, outfd, data): self.table_header(outfd, [ ("Pid", "8"), ("Start", "#018x"), ("End", "#018x"), ("Flags", "6"), ("Pgoff", "[addr]"), ("Major", "6"), ("Minor", "6"), ("Inode", "10"), ("File Path", "80"), ]) for task, vma in data: if vma.vm_file: inode = vma.vm_file.dentry.d_inode major, minor = inode.i_sb.major, inode.i_sb.minor ino = inode.i_ino pgoff = vma.vm_pgoff << 12 fname = linux_common.get_path(task, vma.vm_file) else: (major, minor, ino, pgoff) = [0] * 4 if vma.vm_start <= task.mm.start_brk and vma.vm_end >= task.mm.brk: fname = "[heap]" elif vma.vm_start <= task.mm.start_stack and vma.vm_end >= task.mm.start_stack: fname = "[stack]" else: fname = "" self.table_row(outfd, task.pid, vma.vm_start, vma.vm_end, str(vma.vm_flags), pgoff, major, minor, ino, fname)
def check_open_files_fop(self, f_op_members, modules): # get all the members in file_operations, they are all function pointers openfiles = linux_lsof.linux_lsof(self._config).calculate() for (task, filp, i) in openfiles: for (hooked_member, hook_address) in self.verify_ops(filp.f_op, f_op_members, modules): name = "{0:s} {1:d} {2:s}".format(task.comm, i, linux_common.get_path(task, filp)) yield (name, hooked_member, hook_address)
def generator(self, data): for task in data: for filp, fd in task.lsof(): yield (0, [ int(task.pid), int(fd), str(linux_common.get_path(task, filp)) ])
def check_open_files_fop(self, f_op_members, modules): # get all the members in file_operations, they are all function pointers tasks = linux_pslist.linux_pslist(self._config).calculate() for task in tasks: for filp, i in task.lsof(): for (hooked_member, hook_type, hook_address) in self._is_inline_hooked(filp.f_op, f_op_members, modules): name = "{0:s} {1:d} {2:s}".format(task.comm, i, linux_common.get_path(task, filp)) yield (name, hooked_member, hook_type, hook_address)
def get_data_section(config, what): proc_maps = linux_proc_maps.linux_proc_maps(config).calculate() for task, vma in proc_maps: if not vma.vm_file: continue if not linux_common.get_path(task, vma.vm_file) == what: continue if not (vma.vm_flags & linux_flags.VM_READ and vma.vm_flags & linux_flags.VM_WRITE and not vma.vm_flags & linux_flags.VM_EXEC): continue yield task, vma
def generator(self, data): for task in data: for filp, fd in task.lsof(): yield (0, [ Address(task.obj_offset), str(task.comm), int(task.pid), int(fd), str(linux_common.get_path(task, filp)) ])
def get_map_by_name(self, name, permissions='r-x'): """ Find a memory mapping (vm_area) by its name (not exact match). Optionally, check permissions. @param name: The mapped name to find. @param permissions: Permissions in 'rwx' format @return: A (vm_start, vm_end, libname) tuple or None """ # We use this to find libc for vma in self.task.get_proc_maps(): libname = linux_common.get_path(self.task, vma.vm_file) # just look for partial name if str(vma.vm_flags) == permissions and name in libname: return vma.vm_start, vma.vm_end, libname return None
def render_text(self, outfd, data): self.table_header(outfd, [ ("Offset", "#018x"), ("Name", "30"), ("Pid", "8"), ("FD", "8"), ("Mode", "15"), ("Path", ""), ]) for task in data: for filp, fd in task.lsof(): self.table_row(outfd, Address(task.obj_offset), str(task.comm), task.pid, fd, self.__toModeText(filp.f_mode), linux_common.get_path(task, filp))
def _get_name(self, task, addr): hook_vma = None hookdesc = "<Unknown mapping>" for i in task.get_proc_maps(): if addr >= i.vm_start and addr < i.vm_end: hook_vma = i break if hook_vma: if hook_vma.vm_file: hookdesc = linux_common.get_path(task, hook_vma.vm_file) else: hookdesc = '[{0:x}:{1:x},{2}]'.format(hook_vma.vm_start, hook_vma.vm_end, hook_vma.vm_flags) return (hook_vma, hookdesc)
def render_text(self, outfd, data): self.table_header(outfd, [("Offset","#018x"), ("Name","30"), ("Pid", "8"), ("FD", "8"), ("Mode", "15"), ("Path", ""), ]) for task in data: for filp, fd in task.lsof(): self.table_row(outfd, Address(task.obj_offset), str(task.comm), task.pid, fd, self.__toModeText(filp.f_mode), linux_common.get_path(task, filp))
def render_text(self, outfd, data): self.table_header(outfd, [("Start", "[addrpad]"), ("End", "[addrpad]"), ("Flags", "6"), ("Pgoff", "6"), ("Major", "6"), ("Minor", "6"), ("Inode", "10"), ("File Path", "80"), ]) for task, vma in data: mm = task.mm if vma.vm_file: inode = vma.vm_file.dentry.d_inode sb = obj.Object("super_block", offset = inode.i_sb, vm = self.addr_space) dev = sb.s_dev ino = inode.i_ino pgoff = vma.vm_pgoff << 12 fname = linux_common.get_path(task, vma.vm_file) else: (dev, ino, pgoff) = [0] * 3 if vma.vm_start <= mm.start_brk and vma.vm_end >= mm.brk: fname = "[heap]" elif vma.vm_start <= mm.start_stack and vma.vm_end >= mm.start_stack: fname = "[stack]" else: fname = "" self.table_row(outfd, self.mask_number(vma.vm_start), self.mask_number(vma.vm_end), self.format_perms(vma.vm_flags), pgoff, self.MAJOR(dev), self.MINOR(dev), ino, fname)
def render_text(self, outfd, data): self.table_header(outfd, [ ("Start", "[addrpad]"), ("End", "[addrpad]"), ("Flags", "6"), ("Pgoff", "6"), ("Major", "6"), ("Minor", "6"), ("Inode", "10"), ("File Path", "80"), ]) for task, vma in data: mm = task.mm if vma.vm_file: inode = vma.vm_file.dentry.d_inode sb = obj.Object("super_block", offset=inode.i_sb, vm=self.addr_space) dev = sb.s_dev ino = inode.i_ino pgoff = vma.vm_pgoff << 12 fname = linux_common.get_path(task, vma.vm_file) else: (dev, ino, pgoff) = [0] * 3 if vma.vm_start <= mm.start_brk and vma.vm_end >= mm.brk: fname = "[heap]" elif vma.vm_start <= mm.start_stack and vma.vm_end >= mm.start_stack: fname = "[stack]" else: fname = "" self.table_row(outfd, self.mask_number(vma.vm_start), self.mask_number(vma.vm_end), self.format_perms(vma.vm_flags), pgoff, self.MAJOR(dev), self.MINOR(dev), ino, fname)
def dump_fd_info(self, task, sockets_type): fdinfoFile = open("fdinfo-2.json", "w") entries = [] for file, fd in task.lsof(): path = linux_common.get_path(task, file) element = {"id": 0, "flags": 0, "type": "", "fd": int(fd)} if "/dev/pts" in path: element["id"] = 1 element["type"] = "TTY" elif "socket:[" in path: element["id"] = fd - 1 element["type"] = sockets_type[fd] else: element["id"] = fd - 1 element["type"] = "REG" entries.append(element) data = {"magic": "FDINFO", "entries": entries} fdinfoFile.write(json.dumps(data, indent=4, sort_keys=False)) fdinfoFile.close()
def getShmid(self, progname, current_name, dic, task): if current_name == "" or "[" in current_name: return 0 if progname not in dic: maxFd = 2 for filp, fd in task.lsof(): #self.table_row(outfd, Address(task.obj_offset), str(task.comm), task.pid, fd, linux_common.get_path(task, filp)) if fd > maxFd and "/dev/pts/" not in linux_common.get_path( task, filp): maxFd = fd dic[progname] = maxFd if current_name in dic: return dic[current_name] else: dic[current_name] = len(dic) + dic[progname] return dic[current_name]
def render_text(self, outfd, data): self.table_header(outfd, [("Pid", "8"), ("Start", "#018x"), ("End", "#018x"), ("Flags", "6"), ("Pgoff", "[addr]"), ("Major", "6"), ("Minor", "6"), ("Inode", "10"), ("File Path", "80"), ]) for task, vma in data: if vma.vm_file: inode = vma.vm_file.dentry.d_inode major, minor = inode.i_sb.major, inode.i_sb.minor ino = inode.i_ino pgoff = vma.vm_pgoff << 12 fname = linux_common.get_path(task, vma.vm_file) else: (major, minor, ino, pgoff) = [0] * 4 if vma.vm_start <= task.mm.start_brk and vma.vm_end >= task.mm.brk: fname = "[heap]" elif vma.vm_start <= task.mm.start_stack and vma.vm_end >= task.mm.start_stack: fname = "[stack]" else: fname = "" self.table_row(outfd, task.pid, vma.vm_start, vma.vm_end, str(vma.vm_flags), pgoff, major, minor, ino, fname)
def render_text(self, outfd, data): dt = time.time() target_dport = self._config.dport decision = 'drop' #debug.info("Looking for dport == " + str(target_dport)) # Load process name whitelist whitelist = open("DummyWhiteList.txt").read().splitlines() # Optional checking of hash for for shell code, disable by default global already_done pagesize = 4096 do_hash_checks = False do_expl_checks = False # Iterrate through open file descriptors to look for application # that has an open socket, than match port number to that socket, # and finally compare application name to white list. for (task, filp, i) in data: # Optionally generate hash over each executable if do_hash_checks: if str(task.comm) not in already_done: already_done.append(str(task.comm)) for vma in task.get_proc_maps(): if vma.vm_file: if "r-x" in str(vma.vm_flags): fname = str(linux_common.get_path(task, vma.vm_file)) if str(task.comm) in fname and ".so" not in fname: proc_as = task.get_process_address_space() start = vma.vm_start pages = "" while start < vma.vm_end: pages = pages + proc_as.zread(start, pagesize) start = start + pagesize debug.info("SHA1 for " + str(task.comm) + " is " + str(hashlib.sha1(pages).hexdigest())) break # End if hash checks # Optionally generate hash and check stack and heap for 0x00 sequences (shellcode) if do_expl_checks: if str(task.comm) not in already_done: already_done.append(str(task.comm)) bufpages = "" for vma in task.get_proc_maps(): if vma.vm_file: # first hash if "r-x" in str(vma.vm_flags): fname = str(linux_common.get_path(task, vma.vm_file)) if str(task.comm) in fname and ".so" not in fname: proc_as = task.get_process_address_space() start = vma.vm_start pages = "" while start < vma.vm_end: pages = pages + proc_as.zread(start, pagesize) start = start + pagesize debug.info("SHA1 for " + str(task.comm) + " is " + str(hashlib.sha1(pages).hexdigest())) # heap elif vma.vm_start <= task.mm.start_brk and vma.vm_end >= task.mm.brk: proc_as = task.get_process_address_space() start = vma.vm_start while start < vma.vm_end: bufpages = bufpages + proc_as.zread(start, pagesize) start = start + pagesize # stack elif vma.vm_start <= task.mm.start_stack and vma.vm_end >= task.mm.start_stack: proc_as = task.get_process_address_space() start = vma.vm_start while start < vma.vm_end: bufpages = bufpages + proc_as.zread(start, pagesize) start = start + pagesize hexstr = ":".join("{:02x}".format(ord(c)) for c in bufpages) if "90:90:90:90:90:90:90" in hexstr: debug.info("Shellcode for " + str(task.comm) + " found.") else: debug.info("Shellcode for " + str(task.comm) + " not found.") # End if hash and shell code checks # See if open file is a socket if filp.f_op == self.addr_space.profile.get_symbol("socket_file_ops") or filp.dentry.d_op == self.addr_space.profile.get_symbol("sockfs_dentry_operations"): iaddr = filp.dentry.d_inode skt = self.SOCKET_I(iaddr) inet_sock = obj.Object("inet_sock", offset = skt.sk, vm = self.addr_space) # We are only checking TCP sockets if inet_sock.protocol in ("TCP"): state = inet_sock.state if inet_sock.protocol == "TCP" else "" family = inet_sock.sk.__sk_common.skc_family # We are only checking IPv4 if family == socket.AF_INET: saddr = inet_sock.src_addr if "0.0.0.0" not in str(saddr): sport = inet_sock.src_port dport = inet_sock.dst_port daddr = inet_sock.dst_addr #debug.info("Timestamp " + str(round((time.time() - dt), 2)) + "s") debug.info("Daddr: " + str(daddr) + ", dport: " + str(dport) + ", saddr: " + str(saddr) + ", sport: " + str(sport) + ", status: " + str(state) + ", name: " + str(task.comm) + ", pid: " + str(task.pid)) # The port we are looking for is the destination port of the SYN/ACK packet, # but on the target computer that is the source port of the original SYN # connection, hence compare target_dport (packet) with sport (target) if target_dport == sport: if str(task.comm) in whitelist: decision = 'accept' break # End checking for sockets # End of for loop debug.info("Done in " + str(round((time.time() - dt), 2)) + "s") # Last thing printed is the decision, accept or drop print decision
def calculate(self): linux_common.set_plugin_members(self) elfs = dict() ignore = frozenset(self._config.IGNORE) for task, elf, elf_start, elf_end, soname, needed in linux_elfs.linux_elfs.calculate( self): elfs[(task, soname)] = (elf, elf_start, elf_end, needed) for k, v in elfs.iteritems(): task, soname = k elf, elf_start, elf_end, needed = v if elf._get_typename("hdr") == "elf32_hdr": elf_arch = 32 else: elf_arch = 64 needed_expanded = set([soname]) if (task, None) in elfs: needed_expanded.add(None) # jmp slot can point to ELF itself if the fn hasn't been called yet (RTLD_LAZY) # can point to main binary (None above) if this is a plugin-style symbol while len(needed) > 0: dep = needed.pop(0) needed_expanded.add(dep) try: needed += set(elfs[(task, dep)][3]) - needed_expanded except KeyError: needed_expanded.remove(dep) for reloc in elf.relocations(): rsym = elf.relocation_symbol(reloc) if rsym == None: continue symbol_name = elf.symbol_name(rsym) if symbol_name == None: symbol_name = "<N/A>" offset = reloc.r_offset if offset < elf_start: offset = elf_start + offset if elf_arch == 32: addr = obj.Object("unsigned int", offset=offset, vm=elf.obj_vm) else: addr = obj.Object("unsigned long long", offset=offset, vm=elf.obj_vm) match = False for dep in needed_expanded: _, dep_start, dep_end, _ = elfs[(task, dep)] if addr >= dep_start and addr < dep_end: match = dep hookdesc = '' vma = None for i in task.get_proc_maps(): if addr >= i.vm_start and addr < i.vm_end: vma = i break if vma: if vma.vm_file: hookdesc = linux_common.get_path(task, vma.vm_file) if hookdesc in ignore: continue else: hookdesc = '[{0:x}:{1:x},{2}]'.format( vma.vm_start, vma.vm_end, vma.vm_flags) if hookdesc == "": hookdesc = 'invalid memory' if match != False: if self._config.ALL and match == soname: hookdesc = '[RTLD_LAZY]' hooked = False else: hooked = True yield task, soname, elf, elf_start, elf_end, addr, symbol_name, hookdesc, hooked
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() # Are we dealing with 32 or 64-bit pointers if self.addr_space.profile.metadata.get('memory_model', '32bit') == '32bit': pack_format = "<I" addr_sz = 4 else: pack_format = "<Q" addr_sz = 8 for task in tasks: proc_as = task.get_process_address_space() # In cases when mm is an invalid pointer if not proc_as: continue procvars = [] for vma in task.get_proc_maps(): if not (vma.vm_file and str(vma.vm_flags) == "rw-" and linux_common.get_path( task, vma.vm_file).find("bash") != -1): continue env_start = 0 for off in range(vma.vm_start, vma.vm_end): # check the first index addrstr = proc_as.read(off, addr_sz) if not addrstr or len(addrstr) != addr_sz: continue addr = struct.unpack(pack_format, addrstr)[0] # check first idx... if addr: firstaddrstr = proc_as.read(addr, addr_sz) if not firstaddrstr or len(firstaddrstr) != addr_sz: continue firstaddr = struct.unpack(pack_format, firstaddrstr)[0] buf = proc_as.read(firstaddr, 64) if not buf: continue eqidx = buf.find("=") if eqidx > 0: nullidx = buf.find("\x00") # single char name, = if nullidx >= eqidx: env_start = addr if env_start == 0: continue envars = obj.Object(theType="Array", targetType="Pointer", vm=proc_as, offset=env_start, count=256) for var in envars: if var: varstr = proc_as.read(var, 1600) eqidx = varstr.find("=") idx = varstr.find("\x00") if idx == -1 or eqidx == -1 or idx < eqidx: break varstr = varstr[:idx] procvars.append(varstr) yield task, " ".join(procvars) break
def get_sock_info(self, task, addrspace): sockets_dic = {} sockets = [] flags = [] fds = [] for inode, fd in task.lsof(): path = linux_common.get_path(task, inode) if "socket:[" in path: path = path.replace("[", "") path = path.replace("]", "") ino = path.split(":")[1] fds.append(fd-1) sockets.append(ino) flags.append(inode.f_flags) i = 0 for ents in task.netstat(): if ents[0] == socket.AF_INET: (node, proto, sock_saddr, sock_sport, sock_daddr, sock_dport, state) = ents[1] sock_state = node.sk.__sk_common.skc_state sock_proto = node.sk.sk_protocol sock_family = node.sk.__sk_common.skc_family sock_type = node.sk.sk_type inet_sock = node.cast("inet_connection_sock") sock_backlog = inet_sock.icsk_backoff sock_sndbuf = node.sk.sk_sndbuf sock_rcvbuf = node.sk.sk_rcvbuf if node.sk.__sk_common.skc_reuse == 0: sock_reuseaddr = False else: sock_reuseaddr = True sock_priority = node.sk.sk_priority sock_rowlat = node.sk.sk_rcvlowat sock_mark = node.sk.sk_mark sock_ino = sockets[i] sock_flags = flags[i] sock_id = fds[i] i += 1 sockets_dic[sock_ino] = { "id":int(sock_id), "ino":int(sock_ino), "family":int(sock_family), "type":int(sock_type), "proto":int(sock_proto), "state":int(sock_state), "src_port":int(sock_sport), "dst_port":int(sock_dport), "flags":"{0:#x}".format(sock_flags), "backlog":int(sock_backlog), "src_addr":[str(sock_saddr)], "dst_addr":[str(sock_daddr)], "opts":{ "so_sndbuf":int(sock_sndbuf), "so_rcvbuf":int(sock_rcvbuf), "so_snd_tmo_sec":0, "so_snd_tmo_usec":0, "so_rcv_tmo_sec":0, "so_rcv_tmo_usec":0, "reuseaddr": sock_reuseaddr, "so_priority":int(sock_priority), "so_rcvlowat":int(sock_rowlat), "so_mark":int(sock_mark), "so_passcred": False, "so_passsec":False, "so_dontroute": False, "so_no_check": False }, "ip_opts":{} } if proto == "TCP": # Readig values for tcp_info structure, this structure is used by CRIU, # but it's not a kernel structure, # in kernel source (http://lxr.free-electrons.com/source/net/ipv4/tcp.c#L2644) # values are read using 2 different kernel structures (tcp_sock, inet_connection_sock) # tcp_get_info: http://lxr.free-electrons.com/source/include/linux/tcp.h#L371 tcp_sock = node.cast("tcp_sock") tcp_snd_wscale = tcp_sock.rx_opt.snd_wscale tcp_rcv_wscale = tcp_sock.rx_opt.rcv_wscale tcp_mss_clamp = tcp_sock.rx_opt.mss_clamp tcp_mask = self.get_tcpi_options(tcp_sock) tcp_inq_len = tcp_sock.rcv_nxt - tcp_sock.copied_seq tcp_outq_len = tcp_sock.write_seq - tcp_sock.snd_una tcp_inq_seq = tcp_sock.rcv_nxt tcp_outq_seq = tcp_sock.write_seq tcp_unsq_len = 0 #Not Found tcp_timestamp = 0 #Not Found #Reading receive and write queues #Is not useful because it's impossible to restore a transfer #receiveQueue = self.read_queue(node.sk.sk_receive_queue, addrspace) #writeQueue = self.read_queue(node.sk.sk_write_queue, addrspace) tcp_stream_data = { "inq_len":int(tcp_inq_len), "inq_seq":int(tcp_inq_seq), "outq_len":int(tcp_outq_len), "outq_seq":int(tcp_outq_seq), "opt_mask":"{0:#x}".format(tcp_mask), "snd_wscale":int(tcp_snd_wscale), "mss_clamp":int(tcp_mss_clamp), "rcv_wscale":int(tcp_rcv_wscale), "timestamp":int(tcp_timestamp), "unsq_len":int(tcp_unsq_len), "extra":{"outq":"", "inq":""} #"extra":{"outq":base64.b64encode(str(writeQueue).encode('ascii')), "inq":base64.b64encode(str(receiveQueue).encode('ascii'))} } sockets_dic[sock_ino]["tcp_stream"] = tcp_stream_data return sockets_dic
def generator(self, data): for task in data: for filp, fd in task.lsof(): yield (0, [Address(task.obj_offset),str(task.comm),int(task.pid), int(fd), str(linux_common.get_path(task, filp))])
def generator(self, data): for task in data: for filp, fd in task.lsof(): yield (0, [int(task.pid), int(fd), str(linux_common.get_path(task, filp))])
def calculate(self): linux_common.set_plugin_members(self) elfs = dict() ignore = frozenset(self._config.IGNORE) for task, elf, elf_start, elf_end, soname, needed in linux_elfs.linux_elfs.calculate(self): elfs[(task, soname)] = (elf, elf_start, elf_end, needed) for k, v in elfs.iteritems(): task, soname = k elf, elf_start, elf_end, needed = v if elf._get_typename("hdr") == "elf32_hdr": elf_arch = 32 else: elf_arch = 64 needed_expanded = set([soname]) if (task, None) in elfs: needed_expanded.add(None) # jmp slot can point to ELF itself if the fn hasn't been called yet (RTLD_LAZY) # can point to main binary (None above) if this is a plugin-style symbol while len(needed) > 0: dep = needed.pop(0) needed_expanded.add(dep) try: needed += set(elfs[(task, dep)][3]) - needed_expanded except KeyError: needed_expanded.remove(dep) for reloc in elf.relocations(): rsym = elf.relocation_symbol(reloc) if rsym == None: continue symbol_name = elf.symbol_name(rsym) if symbol_name == None: symbol_name = "<N/A>" offset = reloc.r_offset if offset < elf_start: offset = elf_start + offset if elf_arch == 32: addr = obj.Object("unsigned int", offset = offset, vm = elf.obj_vm) else: addr = obj.Object("unsigned long long", offset = offset, vm = elf.obj_vm) match = False for dep in needed_expanded: _, dep_start, dep_end, _ = elfs[(task, dep)] if addr >= dep_start and addr < dep_end: match = dep hookdesc = '' vma = None for i in task.get_proc_maps(): if addr >= i.vm_start and addr < i.vm_end: vma = i break if vma: if vma.vm_file: hookdesc = linux_common.get_path(task, vma.vm_file) if hookdesc in ignore: continue else: hookdesc = '[{0:x}:{1:x},{2}]'.format(vma.vm_start, vma.vm_end, vma.vm_flags) if hookdesc == "": hookdesc = 'invalid memory' if match != False: if self._config.ALL and match == soname: hookdesc = '[RTLD_LAZY]' hooked = False else: hooked = True yield task, soname, elf, elf_start, elf_end, addr, symbol_name, hookdesc, hooked
def calculate(self): linux_common.set_plugin_members(self) tasks = linux_pslist.linux_pslist(self._config).calculate() # Are we dealing with 32 or 64-bit pointers if self.addr_space.profile.metadata.get('memory_model', '32bit') == '32bit': pack_format = "<I" addr_sz = 4 else: pack_format = "<Q" addr_sz = 8 for task in tasks: proc_as = task.get_process_address_space() # In cases when mm is an invalid pointer if not proc_as: continue procvars = [] for vma in task.get_proc_maps(): if not (vma.vm_file and str(vma.vm_flags) == "rw-" and linux_common.get_path(task, vma.vm_file).find("bash") != -1): continue env_start = 0 for off in range(vma.vm_start, vma.vm_end): # check the first index addrstr = proc_as.read(off, addr_sz) if not addrstr or len(addrstr) != addr_sz: continue addr = struct.unpack(pack_format, addrstr)[0] # check first idx... if addr: firstaddrstr = proc_as.read(addr, addr_sz) if not firstaddrstr or len(firstaddrstr) != addr_sz: continue firstaddr = struct.unpack(pack_format, firstaddrstr)[0] buf = proc_as.read(firstaddr, 64) if not buf: continue eqidx = buf.find("=") if eqidx > 0: nullidx = buf.find("\x00") # single char name, = if nullidx >= eqidx: env_start = addr if env_start == 0: continue envars = obj.Object(theType="Array", targetType="Pointer", vm=proc_as, offset=env_start, count=256) for var in envars: if var: varstr = proc_as.read(var, 1600) eqidx = varstr.find("=") idx = varstr.find("\x00") if idx == -1 or eqidx == -1 or idx < eqidx: break varstr = varstr[:idx] procvars.append(varstr) yield task, " ".join(procvars) break
def render_text(self, outfd, data): if not self._config.PID: debug.error( "You have to specify a process to dump. Use the option -p.\n") file_name = "pages-1.img" file_path = os.path.join(self._config.DUMP_DIR, file_name) progName = "" shmidDic = {} procFiles = {} #Files used in process procFilesExtr = {} #Files that have to be extracted print "Creating pages file for process with PID: " + self._config.PID buildJson = True pagemap = open("pagemap-{0}.json".format(self._config.PID), "w") pagemapData = {"magic": "PAGEMAP", "entries": [{"pages_id": 1}]} mmFile = open("mm-{0}.json".format(self._config.PID), "w") mmData = { "magic": "MM", "entries": [{ "mm_start_code": 0, "mm_end_code": 0, "mm_start_data": 0, "mm_end_data": 0, "mm_start_stack": 0, "mm_start_brk": 0, "mm_brk": 0, "mm_arg_start": 0, "mm_arg_end": 0, "mm_env_start": 0, "mm_env_end": 0, "exe_file_id": 0, "vmas": [], "dumpable": 1 }] } regfilesFile = open("procfiles.json".format(self._config.PID), "w") regfilesData = {"entries": [], "pid": self._config.PID, "threads": []} self.table_header(outfd, [("Start", "#018x"), ("End", "#018x"), ("Number of Pages", "6"), ("File Path", "")]) outfile = open(file_path, "wb") vmas = [] for task, vma in data: savedTask = task (fname, major, minor, ino, pgoff) = vma.info(task) vmas.append(vma) #create heuristic for obtaining progname if "[" not in fname and ".so" not in fname and ino != 0 and ".cache" not in fname: progName = fname for vma in vmas: (fname, major, minor, ino, pgoff) = vma.info(savedTask) vmasData = { "start": "{0:#x}".format(vma.vm_start), "end": "{0:#x}".format(vma.vm_end), "pgoff": pgoff, "shmid": self.getShmid(progName, fname, shmidDic, savedTask), "prot": "{0}".format(self.protText(str(vma.vm_flags))), "flags": "{0}".format(self.flagsText(fname)), "status": "{0}".format(self.statusText(fname)), "fd": -1, "fdflags": "0x0" } #If VDSO number of pages of predecessor node have to be incremented if fname == "[vdso]": mmData["entries"][0]["vmas"][len(mmData["entries"][0]["vmas"]) - 1]["status"] += " | VMA_AREA_VVAR" pagemapData["entries"][len(pagemapData["entries"]) - 1]["nr_pages"] += 2 mmData["entries"][0]["vmas"].append(vmasData) #if Inode != 0, it's a file which have to be linked if ino != 0 and fname not in procFiles: procFiles[fname] = True idF = vmasData["shmid"] typeF = "local" nameF = fname if fname == progName: #ELF is extracted typeF = "elf" nameF = savedTask.comm + ".dump" fileE = {"name": nameF, "id": idF, "type": typeF} regfilesData["entries"].append(fileE) #Shared Lib in exec mode not have to be dumped exLib = ".so" in fname and "x" in str(vma.vm_flags) #DUMP only what CRIU needs if str( vma.vm_flags ) != "---" and fname != "[vdso]" and ".cache" not in fname and not exLib and "/lib/locale/" not in fname: npage = 0 for page in self.read_addr_range_page(savedTask, vma.vm_start, vma.vm_end): outfile.write(page) npage += 1 pagemapData["entries"].append({ "vaddr": "{0:#x}".format(vma.vm_start), "nr_pages": npage }) self.table_row(outfd, vma.vm_start, vma.vm_end, npage, fname) outfile.close() #set Limit addresses for MM file print "Reading address ranges and setting limits" mm = savedTask.mm mmData["entries"][0]["mm_start_code"] = "{0:#x}".format(mm.start_code) mmData["entries"][0]["mm_end_code"] = "{0:#x}".format(mm.end_code) mmData["entries"][0]["mm_start_data"] = "{0:#x}".format(mm.start_data) mmData["entries"][0]["mm_end_data"] = "{0:#x}".format(mm.end_data) mmData["entries"][0]["mm_start_stack"] = "{0:#x}".format( mm.start_stack) mmData["entries"][0]["mm_start_brk"] = "{0:#x}".format(mm.start_brk) mmData["entries"][0]["mm_brk"] = "{0:#x}".format(mm.brk) mmData["entries"][0]["mm_arg_start"] = "{0:#x}".format(mm.arg_start) mmData["entries"][0]["mm_arg_end"] = "{0:#x}".format(mm.arg_end) mmData["entries"][0]["mm_env_start"] = "{0:#x}".format(mm.env_start) mmData["entries"][0]["mm_env_end"] = "{0:#x}".format(mm.env_end) mmData["entries"][0]["exe_file_id"] = shmidDic[progName] #Reading Auxilary Vector print "Reading Auxiliary Vector" saved_auxv = linux_dump_auxv.linux_dump_auxv( self._config).read_auxv(task)[:38] mmData["entries"][0]["mm_saved_auxv"] = saved_auxv #Files used by process: TYPE = EXTRACTED for filp, fd in task.lsof(): fpath = linux_common.get_path(task, filp) if "/dev/pts" not in fpath: if "/" not in fpath: if "socket:[" in fpath: regfilesData["sockets"] = [] regfilesData["sockets"].append(fd - 1) continue else: fname = fpath.replace("/", "_") fname += str(fd) typeF = "extracted" idF = fd - 1 fileE = { "name": fname, "id": idF, "type": typeF, "pos": long(filp.f_pos) } regfilesData["entries"].append(fileE) procFilesExtr[fname] = "{0:#x}".format(filp.f_inode) print "Extracting Files: " self.dumpFile(procFilesExtr, savedTask) print "Building PsTree" self.buildPsTree(savedTask) for thread in savedTask.threads(): regfilesData["threads"].append(int(str(thread.pid))) print "Searching registers values and threads states" self.readRegs(savedTask) pagemap.write(json.dumps(pagemapData, indent=4, sort_keys=False)) pagemap.close() mmFile.write(json.dumps(mmData, indent=4, sort_keys=False)) mmFile.close() regfilesFile.write(json.dumps(regfilesData, indent=4, sort_keys=False)) regfilesFile.close() print "Searching Signal Handler and sigactions" self.dumpSignals(savedTask) sockets_type = {} print "Dumping Sockets" self.dumpSock(savedTask, sockets_type) print "Dumping Unix Sockets" self.dumpUnixSock(savedTask, sockets_type) print "Extracting File Descriptors info" self.dump_fd_info(savedTask, sockets_type) print "Dumping ELF file" self.dumpElf(outfd)