查问题时发现全局变量能读出来会提高效率,于是考虑从怎么读出内核态的全局变量,脚本如下
f = open("/proc/kcore", 'rb')
f.seek(4) # skip magic
assert f.read(1) == b'\x02' # 64 位
def read_number(bytes):
return int.from_bytes(bytes, 'little', signed=False)
elf_header_len = 64
f.seek(elf_header_len - 10)
sec_header_size = read_number(f.read(2))
sec_header_num = read_number(f.read(2))
f.seek(elf_header_len + sec_header_size) # ignore note section
sections = []
for i in range(1, sec_header_num):
sec_header = f.read(sec_header_size)
sections.append({
'offset': hex(read_number(sec_header[8:16])),
'vaddr': hex(read_number(sec_header[16:24])),
'size': hex(read_number(sec_header[32:40])),
})
print(f"section {i}: " + str(sections[-1]))
def addr_to_offset(addr):
for sec in sections:
vaddr = int(sec['vaddr'], 16)
size = int(sec['size'], 16)
if addr >= vaddr and addr < vaddr + size:
return int(sec['offset'], 16) + (addr - vaddr)
raise Exception("ilegel_addr: " + hex(addr))
def read_offset_value(offset, type):
support_types = ['u8', 'u16', 'u32', 'u64', 's8', 's16', 's32', 's64', 'string','x8','x16','x32','x64']
if type not in support_types:
raise Exception("type should be in " + str())
f.seek(offset)
if type == 'string':
ret = b''
ch = f.read(1)
while ch != b'\x00':
ret += ch
ch = f.read(1)
return ret
elif type.startswith('s'):
return int.from_bytes(f.read(int(type[1:]) // 8), 'little', signed=True)
elif type.startswith('u'):
return int.from_bytes(f.read(int(type[1:]) // 8), 'little', signed=False)
else: # 'x'
return hex(int.from_bytes(f.read(int(type[1:]) // 8), 'little', signed=False))
def split_to_three_part(path):
path = path.strip()
prefixes = []
suffixes = []
prefix_bound = path.find('(')
suffix_bound = path.rfind(')')
while prefix_bound != -1:
prefix = eval(path[:prefix_bound])
prefixes.append(prefix)
if suffix_bound == -1:
raise Exception(f"unmatch backet for {path}")
suffix = path[suffix_bound+1:]
suffix = eval(suffix) if suffix else 0
suffixes.append(suffix)
path = path[prefix_bound+1:suffix_bound].strip()
prefix_bound = path.find('(')
suffix_bound = path.rfind(')')
plus_start = path.find('+')
if plus_start == -1:
plus_start = len(path)
minus_start = path.find('-')
if minus_start == -1:
minus_start = len(path)
middle = path[:min(plus_start, minus_start)].strip()
middle_part2 = path[len(middle):]
middle_part2 = eval(middle_part2) if middle_part2 else 0
prefixes.reverse()
suffixes.reverse()
return prefixes, middle, middle_part2, suffixes
while True:
import sys
import re
import os
sys.stdin.flush()
msg = input("输入:").strip()
try:
path, type = msg.split(':')
prefixes, middle, middle_part2, suffixes = split_to_three_part(path)
if middle.startswith('0x') or re.search(r'[a-z,A-Z]+', middle) is None:
start_addr = eval(middle)
else: # is variable name
ret = os.popen("cat /proc/kallsyms | grep \"" + middle + "\" | awk '{print $1,$3}'").read().strip()
if ret == '':
raise Exception("no symbol " + middle + " found, please load module first")
ret = [i.split(' ') for i in ret.split('\n')]
if len(ret) == 1:
start_addr = int(ret[0][0], 16)
else:
find_exact = False
for it in ret:
if it[1] == start_addr:
start_addr = int(it[0], 16)
find_exact = True
break
if not find_exact:
print(f"maybe you means:")
for it in ret:
print(f" {it[1]}")
print(f"find {len(ret)} candidates.")
continue
start_offset = addr_to_offset(start_addr + middle_part2)
for pre, suf in zip(prefixes, suffixes):
start_addr = read_offset_value(start_offset, 'u64')
start_offset = pre + addr_to_offset(start_addr) + suf
print(read_offset_value(start_offset, type))
except Exception as e:
print(e)
输入的格式与 kprobe 的格式类似:+/-偏移(地址)+/-偏移:输出类型
输出类型有:‘u8’, ‘u16’, ‘u32’, ‘u64’, ‘s8’, ‘s16’, ‘s32’, ‘s64’, ‘string’,‘x8’,‘x16’,‘x32’,‘x64’
使用效果如下: