linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

commit 925e7542e4c345ff0ce0efe619b8d594be6978d5
parent f34236640594648fceaea94eb5d760eb061c5156
Author: deurzen <m.deurzen@tum.de>
Date:   Fri,  5 Feb 2021 00:57:45 +0100

adds recursive field access type resolution, array handling

Diffstat:
Mproject/type_dict.py | 50++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 44 insertions(+), 6 deletions(-)

diff --git a/project/type_dict.py b/project/type_dict.py @@ -15,6 +15,7 @@ import json import os, errno import gdb +import re def delfile(name): try: @@ -42,15 +43,13 @@ class CodeDict(): try: self.inf = open(self.in_n, "r") except: - print(f"No file {in_n} found! Run occ.sh first") + print(f"{in_n} file not found, run `occ.sh` first") raise delfile(self.out_n) self.outf = open(self.out_n, "w+") def parse(self): - ignore = ["*", "->", "(", ")", "[", "]"] - line = self.inf.readline() dir = len(line) - 1 if line[-1] == "/" else len(line) @@ -58,13 +57,16 @@ class CodeDict(): # Remove directory prefix, insert ./ to reflect the frame representation of source file in gdb l = ("./" + (line[dir:])).split(" ") + if len(l) < 5 or l[4] != "=": + continue + src = l[0] fn = l[1] lnr = l[2] var = l[3] - if any(s in var for s in ignore): - continue + var = re.split('\-\>|\.', var) + var[0] = re.sub('[.*?]', '', var[0]) if fn == "<global>": try: @@ -73,11 +75,47 @@ class CodeDict(): continue else: try: - type_info = gdb.execute(f"whatis '{fn}'::{var}", to_string = True) + type_info = gdb.execute(f"whatis '{fn}'::{var[0]}", to_string = True) except: continue + if len(var) > 1: + print("looking in", type_info[7:].strip(), "for", var[1:]) + type_info = self.parse_chain(type_info[7:], var, 1) + print("FOUND:", type_info) + if type_info is not None: key = f"{src}:{lnr}" self.dict[key] = type_info.replace('\n','') + + def parse_chain(self, next_type, chain, index): + # we're at the final field access, return its type + if index >= len(chain): + return next_type + + # we need to look for the type of the next field in the field access chain + field = chain[index] + ptype = gdb.execute(f"ptype {next_type}", to_string = True).split("\n")[1:-2] + + # loop over the compound type's fields, attempt to match field we're looking for + for f in ptype: + # account for possible bit field + bitmask = f.rfind(':') + if bitmask > 0: + f = f[:bitmask] + + # account for possible array + f = re.sub('\[.*?\]', '', f) + + # match on field name, everything preceding it is its type + name = re.search(f"[^_A-Za-z0-9]({field})[^_A-Za-z0-9]", f) + + # field name was found, extract type and recurse if necessary + if bool(name): + return self.parse_chain(f[:name.start(1)], chain, index + 1) + + # field not found + return None + + CodeDict()