70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
import subprocess
|
|
|
|
DEVICE = '/dev/nbd0'
|
|
|
|
def fls(inode):
|
|
try:
|
|
r = subprocess.run(['fls', DEVICE, str(inode)],
|
|
capture_output=True, text=True, timeout=30)
|
|
return r.stdout
|
|
except:
|
|
return ''
|
|
|
|
def get_parent_and_name(inode):
|
|
output = fls(inode)
|
|
parent = None
|
|
children = []
|
|
for line in output.splitlines():
|
|
try:
|
|
parts = line.split(None, 2)
|
|
if len(parts) < 3: continue
|
|
type_str = parts[0]
|
|
ino = int(parts[1].rstrip(':').lstrip('*'))
|
|
name = parts[2].strip()
|
|
if name == '..':
|
|
parent = ino
|
|
elif name != '.':
|
|
children.append((type_str[0], ino, name))
|
|
except: continue
|
|
return parent, children
|
|
|
|
# Walk up from pterodactyl
|
|
print('Walking up from pterodactyl (inode 1574102)...')
|
|
chain = [(1574102, 'pterodactyl')]
|
|
inode = 1574102
|
|
for _ in range(20):
|
|
parent, _ = get_parent_and_name(inode)
|
|
if parent is None or parent == inode:
|
|
break
|
|
grp = (parent-1)//8192
|
|
# Get parent's name by looking at its own .. entry
|
|
parent_parent, parent_children = get_parent_and_name(parent)
|
|
# Find our name in parent's listing
|
|
name = f'inode_{parent}'
|
|
for t,i,n in parent_children:
|
|
if i == inode:
|
|
name = n
|
|
break
|
|
status = 'INTACT' if grp >= 13 else 'LOST'
|
|
print(f' [{status}] inode {parent} = {name!r} (group {grp})')
|
|
chain.append((parent, name))
|
|
if grp < 13:
|
|
print(f' Reached zeroed region at inode {parent} - stopping')
|
|
break
|
|
inode = parent
|
|
|
|
print()
|
|
print('Chain (bottom to top):')
|
|
for ino, name in chain:
|
|
print(f' {name} (inode {ino})')
|
|
|
|
# The highest intact inode is our extraction root
|
|
top_inode, top_name = chain[-1]
|
|
grp = (top_inode-1)//8192
|
|
if grp >= 13:
|
|
print(f'\nExtraction root: inode {top_inode} ({top_name!r})')
|
|
else:
|
|
# Use second to last
|
|
top_inode, top_name = chain[-2]
|
|
print(f'\nExtraction root: inode {top_inode} ({top_name!r})')
|