Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PANDA support #9

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified HeapInspect.py
100755 → 100644
Empty file.
76 changes: 76 additions & 0 deletions PANDAHeapInspect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import argparse
from heapinspect.core import *
from pandare import Panda

if __name__ == '__main__':
parser = argparse.ArgumentParser(
prog='HeapInspect.py',
description='''Inspect your heap by a given pid.
Author:lacraig2 (forked from matrix1001)
Github:https://github.com/lacraig2/pandaheapinspect (forked from https://github.com/matrix1001/heapinspect)''')
parser.add_argument(
'--raw',
action='store_true',
help='show more detailed chunk info'
)
parser.add_argument(
'--rela',
action='store_true',
help='show relative detailed chunk info'
)
parser.add_argument(
'-x',
action='store_false',
help='''ignore: heapchunks'''
)

args = parser.parse_args()

panda = Panda(generic="x86_64")

@panda.hook_symbol("libc","malloc")
def hook(cpu, tb, h):
print(f"Caught libc:malloc in {panda.get_process_name(cpu)}")
try:
global pid, args
arena_info = {"main_arena_offset": 4111432,"tcache_enable": True}
hi = HeapInspector(0,panda=panda,arena_info=arena_info)
if args.rela:
hs = HeapShower(hi)
hs.relative = True
if args.x:
print(hs.heap_chunks)
print(hs.fastbins)
print(hs.unsortedbins)
print(hs.smallbins)
print(hs.largebins)
print(hs.tcache_chunks)
elif args.raw:
hs = HeapShower(hi)
if args.x:
print(hs.heap_chunks)
print(hs.fastbins)
print(hs.unsortedbins)
print(hs.smallbins)
print(hs.largebins)
print(hs.tcache_chunks)
else:
pp = PrettyPrinter(hi)
print(pp.all)
except Exception as e:
raise e
h.enabled = False
panda.end_analysis()




@panda.queue_async
def runner():
panda.revert_sync("root")
panda.run_serial_cmd("ls -la && sleep 10")
panda.end_analysis()


panda.run()

7 changes: 4 additions & 3 deletions heapinspect/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ class HeapInspector(object):
Raises:
NotImplementedError: for none supported arch.
'''
def __init__(self, pid):
def __init__(self, pid, panda=None, arena_info=None):
self.pid = pid
self.proc = Proc(pid)
self.panda = panda
self.proc = Proc(pid, panda)
self.arch = self.proc.arch
self.path = self.proc.path
self.libc_path = self.proc.libc
Expand All @@ -51,7 +52,7 @@ def __init__(self, pid):
else:
raise NotImplementedError('invalid arch')

libc_info = get_libc_info(self.libc_path, self.proc.ld)
libc_info = get_libc_info(self.proc.arch,self.libc_path, self.proc.ld,arena_info,panda)
self.libc_version = libc_info['version']
self.tcache_enable = libc_info['tcache_enable']
self.main_arena_offset = libc_info['main_arena_offset']
Expand Down
27 changes: 17 additions & 10 deletions heapinspect/libc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ def build_helper(out_dir, size_t=8):
return out_path


def get_libc_version(path):
def get_libc_version(path,panda=None):
'''Get the libc version.

Args:
path (str): Path to the libc.
Returns:
str: Libc version. Like '2.29', '2.26' ...
'''
content = subprocess.Popen(['strings', path], stdout=subprocess.PIPE).stdout.read()
content = content.decode() #py3 problem
if panda:
content = path
else:
content = subprocess.Popen(['strings', path], stdout=subprocess.PIPE).stdout.read()
content = content.decode() #py3 problem
pattern = "libc[- ]([0-9]+\.[0-9]+)"
result = re.findall(pattern, content)
if result:
Expand All @@ -49,7 +52,7 @@ def get_libc_version(path):
return ""


def get_arena_info(libc_path, ld_path):
def get_arena_info(arch,libc_path, ld_path, panda=None):
'''Get the main arena infomation of the libc.

Args:
Expand All @@ -59,8 +62,9 @@ def get_arena_info(libc_path, ld_path):
dict: like {'main_arena_offset':0x1e430, 'tcache_enable':False}
'''
cur_dir = os.path.dirname(os.path.realpath(__file__))
arch = get_arch(libc_path)
libc_version = get_libc_version(libc_path)
if not panda:
arch = get_arch(libc_path)
libc_version = get_libc_version(libc_path,panda=panda)
dir_path = tempfile.mkdtemp()
# use this to build helper
# helper_path = build_helper(dir_path, size_t=size_t)
Expand Down Expand Up @@ -113,7 +117,7 @@ def get_arena_info(libc_path, ld_path):
# dc = json.JSONDecoder()
# return dc.decode(result)

def get_libc_info(libc_path, ld_path):
def get_libc_info(arch,libc_path, ld_path, arena_info=None, panda=None):
'''Get the infomation of the libc.

Args:
Expand All @@ -123,15 +127,18 @@ def get_libc_info(libc_path, ld_path):
dict: like {'main_arena_offset':0x1e430, 'tcache_enable':True,
'version':2.27}
'''
arch = get_arch(libc_path)
#arch = get_arch(libc_path)
if arch == '64':
size_t = 8
elif arch == '32':
size_t = 4
else:
raise NotImplementedError
info = {'version': get_libc_version(libc_path)}
info.update(get_arena_info(libc_path, ld_path))
info = {'version': get_libc_version(libc_path, panda=panda)}
if arena_info:
info.update(arena_info)
else:
info.update(get_arena_info(arch, libc_path,ld_path,panda=panda))
# malloc_state adjust
if info['version'] in ['2.27', '2.28']:
info['main_arena_offset'] -= size_t
Expand Down
98 changes: 66 additions & 32 deletions heapinspect/proc.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def isin(self, addr):
return addr >= self.start and addr < self.end


def vmmap(pid):
def vmmap(pid, panda = None):
'''Get the vmmap of a process.

Note:
Expand All @@ -70,20 +70,25 @@ def vmmap(pid):
'''

maps = []
mpath = "/proc/%s/maps" % pid
# 00400000-0040b000 r-xp 00000000 08:02 538840 /path/to/file
pattern = re.compile(
"([0-9a-f]*)-([0-9a-f]*) ([rwxps-]*)(?: [^ ]*){3} *(.*)"
)
out = open(mpath).read()
matches = pattern.findall(out)
if matches:
for (start, end, perm, mapname) in matches:
start = int("0x%s" % start, 16)
end = int("0x%s" % end, 16)
if mapname == "":
mapname = "mapped"
maps.append(Map(start, end, perm, mapname))
if panda:
for mapping in panda.get_mappings(panda.get_cpu()):
# we don't know the permissions
maps.append(Map(mapping.base, mapping.base+mapping.size, 'rwx', panda.ffi.string(mapping.name).decode()))
else:
mpath = "/proc/%s/maps" % pid
# 00400000-0040b000 r-xp 00000000 08:02 538840 /path/to/file
pattern = re.compile(
"([0-9a-f]*)-([0-9a-f]*) ([rwxps-]*)(?: [^ ]*){3} *(.*)"
)
out = open(mpath).read()
matches = pattern.findall(out)
if matches:
for (start, end, perm, mapname) in matches:
start = int("0x%s" % start, 16)
end = int("0x%s" % end, 16)
if mapname == "":
mapname = "mapped"
maps.append(Map(start, end, perm, mapname))
return maps


Expand All @@ -96,21 +101,32 @@ class Proc(object):
Args:
pid (int): The pid of the process.
'''
def __init__(self, pid):
def __init__(self, pid, panda=None):
self.pid = pid
self.arch = get_arch(self.path)
self.panda = panda # even if none
if self.panda:
if self.panda.arch_name == "i386": #get_arch(self.path)
self.arch = '32'
elif self.panda.arch_name == "x86_64":
self.arch = '64'
else:
raise NotImplementedError("Other architectures probably work, but just haven't checked")
else:
self.arch = get_arch(self.path)

@property
def path(self):
'''str: The path to the binary of the process.
'''
if self.panda:
return "panda"
return os.path.realpath("/proc/{}/exe".format(self.pid))

@property
def vmmap(self):
'''list(`Map`): The vmmap of the process.
'''
return vmmap(self.pid)
return vmmap(self.pid, self.panda)

def _range_merge(self, lst, range_vec):
merged = 0
Expand Down Expand Up @@ -265,16 +281,34 @@ def read(self, addr, size):
Returns:
str: The readed memory. return '' if error.
'''
mem = "/proc/{}/mem".format(self.pid)
f = open(mem, 'rb')
f.seek(addr)
try:
result = f.read(size)
except:
result = ""
print("error reading: {}:{}".format(hex(addr), hex(size)))
f.close()
return result
if self.panda:
any_output = False
output = b""
while len(output) < size:
try:
cpu = self.panda.get_cpu()
if size > 0x1000:
output += self.panda.virtual_memory_read(cpu, addr, 0x1000)
else:
output += self.panda.virtual_memory_read(cpu, addr, size-len(output))
any_output = True
except:
#print(f"couldn't read {addr:x}-{addr+0x1000:x}")
output += b"\x00"*0x1000
addr += 0x1000
return output if any_output else ''
else:
mem = "/proc/{}/mem".format(self.pid)
f = open(mem, 'rb')
f.seek(addr)
try:
result = f.read(size)
except:
result = ""
print("error reading: {}:{}".format(hex(addr), hex(size)))
f.close()
return result


def search_in_prog(self, search):
'''Search in prog.
Expand Down Expand Up @@ -326,7 +360,7 @@ def search_in_all(self, search):
'''
result = []
ignore_list = ['[vvar]', '[vsyscall]']
for m in vmmap(self.pid):
for m in vmmap(self.panda,self.pid):
if "r" in m.perm and m.mapname not in ignore_list:
result += self.searchmem(m.start, m.end, search)
return result
Expand All @@ -342,7 +376,7 @@ def searchmem_by_mapname(self, mapname, search):
'''
result = []
maps = []
for m in vmmap(self.pid):
for m in vmmap(self.panda,self.pid):
if m.mapname == mapname:
maps.append(m)
for m in maps:
Expand Down Expand Up @@ -432,7 +466,7 @@ def libc(self):
Raises:
Exception: if cannot find the glibc.
'''
for m in vmmap(self.pid):
for m in vmmap(self.pid,panda=self.panda):
if re.match(LIBC_REGEX, m.mapname):
return m.mapname
raise Exception('cannot find libc path')
Expand All @@ -444,7 +478,7 @@ def ld(self):
Raises:
Exception: if cannot find ld path.
'''
for m in vmmap(self.pid):
for m in vmmap(self.pid,panda=self.panda):
if re.match(LD_REGEX, m.mapname):
return m.mapname
raise Exception('cannot find ld path')