Files
2025-03-13 16:17:53 +01:00

139 lines
4.5 KiB
Python
Executable File

"""
summary: Add missing inodes in dirtree structure in "/lost+found" directory
description:
How to collects inodes for various dirtrees ('Local Types', 'Names',
'Functions', 'Imports', 'Address bookmark' and 'Breakpoints') in both
the dirtree structure and the database.
Create a directory an links inodes to them in the dirtree structure
You cannot add all inodes to the "/lost+found" directory because inodes
can be at various places in a dirtree structure.
The early dirtree implementations were not perfect and may have missed to
add some inodes in the tree structure, this script corrects this issue.
keywords: dirtree, recovery
"""
import ida_dirtree
from ida_dirtree import dirtree_t
import ida_typeinf
import ida_funcs
import ida_name
import ida_dbg
import ida_nalt
import ida_kernwin
import ida_moves
def get_ltypes_inode(n):
inode = n + 1
name = ida_typeinf.get_numbered_type_name(None, inode)
if name is None:
# if the name is None, we have a #deleted type,
# present in flat mode but not in fulltree mode
return True, None
else:
return False, inode
def get_func_inode(n):
inode = ida_funcs.getn_func(n).start_ea
return False, inode
def get_name_inode(n):
inode = ida_name.get_nlist_ea(n)
if inode is None:
print(hex(inode))
return False, inode
list_inodes_in_idb = []
def get_import_list():
def imp_cb(ea, name, ordinal):
list_inodes_in_idb.append(ea)
return True
nimps = ida_nalt.get_import_module_qty()
for i in range(nimps):
ida_nalt.enum_import_names(i, imp_cb)
list_inodes_in_idb.sort()
return len(list_inodes_in_idb)
def get_nth_inode_in_idb(n):
return False, list_inodes_in_idb[n]
def get_idaplace_list_inodes_in_idb():
id = ida_kernwin.get_place_class_id("idaplace_t")
p = ida_kernwin.get_place_class_template(id)
r = ida_moves.renderer_info_t()
l = ida_moves.lochist_entry_t(p, r)
for idx in range(ida_moves.bookmarks_t.size(l, None)):
ida_moves.bookmarks_t.get(l, idx, None)
idaloc = ida_kernwin.place_t.as_idaplace_t(l.place())
list_inodes_in_idb.append(idaloc.ea)
list_inodes_in_idb.sort()
return len(list_inodes_in_idb)
def get_bpt_inode(n):
bpt = ida_dbg.bpt_t()
if not ida_dbg.getn_bpt(n, bpt):
return True, None
return False, bpt.bptid
class dt_collect_inode_t(ida_dirtree.dirtree_visitor_t):
def __init__(self):
ida_dirtree.dirtree_visitor_t.__init__(self)
self.list_inodes_in_dirtree = []
def visit(self, c, de):
if dirtree_t.isfile(de):
self.list_inodes_in_dirtree.append(de.idx)
return 0
std_recovery_data = [
( ida_dirtree.DIRTREE_LOCAL_TYPES, ida_typeinf.get_ordinal_count, get_ltypes_inode, "Local Types" ),
( ida_dirtree.DIRTREE_FUNCS, ida_funcs.get_func_qty, get_func_inode, "Local Types" ),
( ida_dirtree.DIRTREE_NAMES, ida_name.get_nlist_size, get_name_inode, "Names" ),
( ida_dirtree.DIRTREE_IMPORTS, get_import_list, get_nth_inode_in_idb, "Imports" ),
( ida_dirtree.DIRTREE_IDAPLACE_BOOKMARKS, get_idaplace_list_inodes_in_idb, get_nth_inode_in_idb, "Address bookmarks" ),
( ida_dirtree.DIRTREE_BPTS, ida_dbg.get_bpt_qty, get_bpt_inode, "Breakpoints" ),
]
for dt_id, get_count_and_maybe_list_inodes_in_idb, get_inode, title in std_recovery_data:
dt = ida_dirtree.get_std_dirtree(dt_id)
print("### Dirtree ", title)
list_inodes_in_idb = []
collector = dt_collect_inode_t()
dt.traverse(collector)
set_inodes_in_dirtree = set(collector.list_inodes_in_dirtree)
nb_inodes = get_count_and_maybe_list_inodes_in_idb()
nb_not_list = 0
nb_list = len(set_inodes_in_dirtree)
print("Number of unique inodes in the idb: %d\nNumber of unique inodes in fulltree mode: %d"%(nb_inodes, nb_list))
if nb_list < nb_inodes:
err = dt.mkdir("/lost+found")
if err != ida_dirtree.DTE_OK:
print(dt.errstr(err))
err = dt.chdir("/lost+found")
if err != ida_dirtree.DTE_OK:
print(dt.errstr(err))
for n in range(nb_inodes):
inc, inode = get_inode(n)
if inode is not None:
if inode not in set_inodes_in_dirtree:
err = dt.link(inode)
if err != ida_dirtree.DTE_OK:
print("Failed to link inode ", hex(inode), " ", dt.errstr(err) )
inc = True
if inc:
nb_not_list += 1
if nb_inodes == nb_list + nb_not_list:
break