|
|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from re import match
|
|
|
|
|
from re import search
|
|
|
|
|
from struct import unpack_from
|
|
|
|
|
|
|
|
|
|
from pwnlib.util.packing import p16
|
|
|
|
|
@ -48,11 +50,20 @@ class Kallsyms:
|
|
|
|
|
|
|
|
|
|
self.rbase_offset = 0
|
|
|
|
|
|
|
|
|
|
self.is_big_endian = None
|
|
|
|
|
|
|
|
|
|
self.token_table = self.find_token_table()
|
|
|
|
|
# TODO: if self.token_table is None its maybe an uncompressed kallsyms
|
|
|
|
|
self.is_uncompressed = self.token_table is None
|
|
|
|
|
|
|
|
|
|
if self.is_uncompressed:
|
|
|
|
|
print(M.info("Detected Uncompressed Kallsyms"))
|
|
|
|
|
self.find_names_uncompressed()
|
|
|
|
|
self.markers = self.find_markers_uncompressed()
|
|
|
|
|
else:
|
|
|
|
|
print(M.info("Detected Compressed Kallsyms"))
|
|
|
|
|
self.token_index = self.find_token_index()
|
|
|
|
|
self.markers = self.find_markers()
|
|
|
|
|
|
|
|
|
|
self.token_index = self.find_token_index()
|
|
|
|
|
self.markers = self.find_markers()
|
|
|
|
|
self.num_syms = self.find_num_syms()
|
|
|
|
|
self.offsets = self.find_offsets()
|
|
|
|
|
|
|
|
|
|
@ -112,7 +123,7 @@ class Kallsyms:
|
|
|
|
|
if len(ascii_candidates) == 1:
|
|
|
|
|
candidates = ascii_candidates
|
|
|
|
|
elif len(candidates) == 0:
|
|
|
|
|
print(M.error("No candidates for token_table"))
|
|
|
|
|
# print(M.error("No candidates for token_table, maybe uncompressed kallsyms"))
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
position = candidates[0]
|
|
|
|
|
@ -283,7 +294,7 @@ class Kallsyms:
|
|
|
|
|
"""
|
|
|
|
|
forward_search = self.kernel_version >= (6, 4)
|
|
|
|
|
|
|
|
|
|
if forward_search:
|
|
|
|
|
if forward_search and not self.is_uncompressed:
|
|
|
|
|
position = self.token_index
|
|
|
|
|
self.is_offsets = True
|
|
|
|
|
else:
|
|
|
|
|
@ -387,15 +398,143 @@ class Kallsyms:
|
|
|
|
|
self.kallsyms[name[1:]] = (addr, name[0])
|
|
|
|
|
|
|
|
|
|
def get_token_table(self):
|
|
|
|
|
tokens = []
|
|
|
|
|
position = self.token_table
|
|
|
|
|
if not self.is_uncompressed:
|
|
|
|
|
tokens = []
|
|
|
|
|
position = self.token_table
|
|
|
|
|
|
|
|
|
|
for num_token in range(256):
|
|
|
|
|
token = ""
|
|
|
|
|
while self.kernel_ro_mem[position]:
|
|
|
|
|
token += chr(self.kernel_ro_mem[position])
|
|
|
|
|
position += 1
|
|
|
|
|
position += 1
|
|
|
|
|
tokens.append(token)
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
tokens = [chr(i) for i in range(256)]
|
|
|
|
|
|
|
|
|
|
return tokens
|
|
|
|
|
|
|
|
|
|
def find_names_uncompressed(self):
|
|
|
|
|
# Find the length byte-separated symbol names
|
|
|
|
|
ksymtab_match = search(
|
|
|
|
|
rb"(?:[\x05-\x23][TWtbBrRAdD][a-z0-9_.]{4,34}){14}", self.kernel_ro_mem
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not ksymtab_match:
|
|
|
|
|
print(M.error("Failed to find kallsyms"))
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
kallsyms_names__offset = ksymtab_match.start(0)
|
|
|
|
|
|
|
|
|
|
# Count the number of symbol names
|
|
|
|
|
position = kallsyms_names__offset
|
|
|
|
|
num_syms = 0
|
|
|
|
|
|
|
|
|
|
while position + 1 < len(self.kernel_ro_mem):
|
|
|
|
|
if (
|
|
|
|
|
self.kernel_ro_mem[position] < 2
|
|
|
|
|
or chr(self.kernel_ro_mem[position + 1]).lower() not in "abdrtvwginpcsu-?"
|
|
|
|
|
):
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
symbol_name_and_type = self.kernel_ro_mem[
|
|
|
|
|
position + 1 : position + 1 + self.kernel_ro_mem[position]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
if not match(rb"^[\x21-\x7e]+$", symbol_name_and_type):
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
position += 1 + self.kernel_ro_mem[position]
|
|
|
|
|
num_syms += 1
|
|
|
|
|
|
|
|
|
|
if num_syms < 100:
|
|
|
|
|
print(M.error("Failed to find kallsyms"))
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
self.end_of_kallsyms_names_uncompressed = position
|
|
|
|
|
|
|
|
|
|
def find_markers_uncompressed(self):
|
|
|
|
|
"""
|
|
|
|
|
This function searches for the kallsyms_markers structure in the kernel memory
|
|
|
|
|
Original Source: https://github.com/marin-m/vmlinux-to-elf/blob/master/vmlinux_to_elf/kallsyms_finder.py
|
|
|
|
|
"""
|
|
|
|
|
position = self.end_of_kallsyms_names_uncompressed
|
|
|
|
|
position += -position % 4
|
|
|
|
|
|
|
|
|
|
max_number_of_space_between_two_nulls = 0
|
|
|
|
|
|
|
|
|
|
# Go just after the first chunk of non-null bytes
|
|
|
|
|
|
|
|
|
|
for num_token in range(256):
|
|
|
|
|
token = ""
|
|
|
|
|
while self.kernel_ro_mem[position]:
|
|
|
|
|
token += chr(self.kernel_ro_mem[position])
|
|
|
|
|
# while position + 1 < len(self.kernel_img) and self.kernel_img[position + 1] == 0:
|
|
|
|
|
# position += 1
|
|
|
|
|
|
|
|
|
|
while position + 1 < len(self.kernel_ro_mem) and self.kernel_ro_mem[position + 1] == 0:
|
|
|
|
|
position += 1
|
|
|
|
|
|
|
|
|
|
for null_separated_bytes_chunks in range(20):
|
|
|
|
|
num_non_null_bytes = 1 # we always start at a non-null byte in this loop
|
|
|
|
|
num_null_bytes = (
|
|
|
|
|
1 # we will at least encounter one null byte before the end of this loop
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
position += 1
|
|
|
|
|
assert position >= 0
|
|
|
|
|
|
|
|
|
|
if self.kernel_ro_mem[position] == 0:
|
|
|
|
|
break
|
|
|
|
|
num_non_null_bytes += 1
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
position += 1
|
|
|
|
|
assert position >= 0
|
|
|
|
|
|
|
|
|
|
if self.kernel_ro_mem[position] != 0:
|
|
|
|
|
break
|
|
|
|
|
num_null_bytes += 1
|
|
|
|
|
|
|
|
|
|
max_number_of_space_between_two_nulls = max(
|
|
|
|
|
max_number_of_space_between_two_nulls, num_non_null_bytes + num_null_bytes
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
max_number_of_space_between_two_nulls % 2 == 1
|
|
|
|
|
): # There may be a leap to a shorter offset in the latest processed entries
|
|
|
|
|
max_number_of_space_between_two_nulls -= 1
|
|
|
|
|
|
|
|
|
|
if max_number_of_space_between_two_nulls not in (2, 4, 8):
|
|
|
|
|
print(M.error("Could not guess the architecture register size for kernel"))
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
self.offset_table_element_size = max_number_of_space_between_two_nulls
|
|
|
|
|
|
|
|
|
|
# Once the size of a long has been guessed, use it to find
|
|
|
|
|
# the first offset (0)
|
|
|
|
|
|
|
|
|
|
position = self.end_of_kallsyms_names_uncompressed
|
|
|
|
|
position += -position % 4
|
|
|
|
|
|
|
|
|
|
# Go just at the first non-null byte
|
|
|
|
|
while position < len(self.kernel_ro_mem) and self.kernel_ro_mem[position] == 0:
|
|
|
|
|
position += 1
|
|
|
|
|
tokens.append(token)
|
|
|
|
|
|
|
|
|
|
return tokens
|
|
|
|
|
likely_is_big_endian = position % self.offset_table_element_size > 1
|
|
|
|
|
if self.is_big_endian is None: # Manual architecture specification
|
|
|
|
|
self.is_big_endian = likely_is_big_endian
|
|
|
|
|
|
|
|
|
|
if position % self.offset_table_element_size == 0:
|
|
|
|
|
position += self.offset_table_element_size
|
|
|
|
|
else:
|
|
|
|
|
position += -position + self.offset_table_element_size
|
|
|
|
|
|
|
|
|
|
position -= self.offset_table_element_size
|
|
|
|
|
position -= self.offset_table_element_size
|
|
|
|
|
|
|
|
|
|
position -= position % self.offset_table_element_size
|
|
|
|
|
|
|
|
|
|
self.kallsyms_markers__offset = position
|
|
|
|
|
|
|
|
|
|
# print('Found kallsyms_markers at file offset 0x%08x' % position)
|
|
|
|
|
|
|
|
|
|
return position
|
|
|
|
|
|