Как обратить виртуальный адрес строки из дампа памяти?

Я пытаюсь найти конкретную строку в памяти процесса. В частности, я хочу найти виртуальный адрес, где он хранится. Я написал скрипт на Python для вызова gcore на процесс и сканировать полученный файл на все совпадения. Тогда я звоню pmap и перебирать записи там. Моя идея состоит в том, чтобы найти раздел памяти, которому соответствует каждый индекс, затем вычесть сумму размеров предыдущих разделов, чтобы получить смещение в правильном разделе, добавить его в базу и получить виртуальный адрес. Однако, когда я ищу строки по виртуальным адресам, которые я вычисляю, используя gdb, я не нахожу правильные строки. Почему мой метод не работает? Есть ли gcore не сбрасывать все содержимое виртуальной памяти по порядку?

#!/usr/bin/python3
import sys
import ctypes
import ctypes.util
import subprocess
import os
import ptrace
import re

if(len(sys.argv) != 2):
    print("Usage: search_and_replace.py target_pid")
    sys.exit(-1)

pid = sys.argv[1]
if not pid.isdigit():
    print("Invalid PID specified.  Make sure PID is an integer")
    sys.exit(-1)

bash_cmd = "sudo gcore -a {}".format(pid)
os.system(bash_cmd)

with open("core." + sys.argv[1], 'rb') as f:
    s = f.read()
# with open("all.dump", 'rb') as f:
#   s = f.read()

str_query = b'a random string in program\'s memory'
str_replc = b'This is an inserted string, replacing the original.'
indices = []
for match in re.finditer(str_query, s):
    indices.append(match.start())
print("number of indices is " + str(len(indices)))

#index = s.find(str_query)

# print("offset is " + str(index))
# if(index == 0):
#   print("error: String not found")
#   sys.exit(-1)

bash_cmd = "sudo pmap -x {} > maps".format(pid)
print(bash_cmd)
subprocess.call(bash_cmd, shell=True)

with open("maps") as m:
    lines = m.readlines()

#calculate the virtual address of the targeted string the running process via parsing the pmap output
pages = []
v_addrs = []

for index in indices:
    sum = 0
    offset = 0
    v_addr = 0  
    #print(index)
    for i in range(2, len(lines) - 2):
        line = lines[i]
        items = line.split()
        v_addr = int(items[0], 16)
        old_sum = sum
        sum += int(items[1]) * 1024
        if sum > index:
            offset = index - old_sum
            print("max is " + hex(v_addr + int(items[1]) * 1024))
            print("offset is " + str(offset) + " hex " + hex(offset))
            print("final va is " + hex(v_addr + offset))
            pages.append(hex(v_addr) + ", " + hex(v_addr + int(items[1]) * 1024))
            v_addrs.append(hex(v_addr + offset))
            break

print("base va is " + hex(v_addr))
v_addr += offset

for page in set(pages):
    print(page)

for va in v_addrs:
    print(va)

Что касается примечания, я также попытался использовать gdb для сканирования файла вручную - похоже, он не находит столько совпадений, когда я использую его find команда для поиска строки в области памяти (точные числа сильно различаются). Это почему?

1 ответ

Решение

Вы можете использовать код Python для поиска различных вещей в файлах ядра. В состав пакета входит elf модуль которого Elf класс предоставляет методы для этого. Следующий вывод из gdb Сессия имеет примеры того, как использовать этот код.

Первая выдержка из этой сессии показывает gdb открытие файла ядра, который был сгенерирован gcore и предоставление некоторых данных для последующих поисков.

18:33:00 $ gdb -q /home/efuller/gnu/bin/gdb core.17856 
Reading symbols from /home/efuller/gnu/bin/gdb...done.
[New LWP 17856]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Core was generated by `/home/efuller/gnu/bin/gdb /home/efuller/gnu/bin/gdb'.
Program terminated with signal SIGINT, Interrupt.
#0  0x00007ffff62c5660 in __poll_nocancel () at ../sysdeps/unix/syscall-template.S:84
84  ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) backtrace
#0  0x00007ffff62c5660 in __poll_nocancel () at ../sysdeps/unix/syscall-template.S:84
#1  0x00005555557f7ea6 in gdb_wait_for_event (block=1) at event-loop.c:772
#2  0x00005555557f7185 in gdb_do_one_event () at event-loop.c:347
#3  0x00005555557f71bd in start_event_loop () at event-loop.c:371
#4  0x00005555557f003a in captured_command_loop (data=0x0) at main.c:324
#5  0x00005555557eb2e9 in catch_errors (func=0x5555557efff8 <captured_command_loop(void*)>, func_args=0x0, errstring=0x555555b4f733 "", mask=RETURN_MASK_ALL) at exceptions.c:236
#6  0x00005555557f16e2 in captured_main (data=0x7fffffffea10) at main.c:1149
#7  0x00005555557f170b in gdb_main (args=0x7fffffffea10) at main.c:1159
#8  0x00005555555f2daa in main (argc=2, argv=0x7fffffffeb18) at gdb.c:32
(gdb) frame 6
#6  0x00005555557f16e2 in captured_main (data=0x7fffffffea10) at main.c:1149
1149              catch_errors (captured_command_loop, 0, "", RETURN_MASK_ALL);
(gdb) info locals
context = 0x7fffffffea10
argc = 2
argv = 0x7fffffffeb18
quiet = 0
set_args = 0
inhibit_home_gdbinit = 0
symarg = 0x7fffffffed8e "/home/efuller/gnu/bin/gdb"
execarg = 0x7fffffffed8e "/home/efuller/gnu/bin/gdb"
pidarg = 0x0
corearg = 0x0
pid_or_core_arg = 0x0
cdarg = 0x0
ttyarg = 0x0
print_help = 0
print_version = 0
print_configuration = 0
cmdarg_vec = 0x0
cmdarg_p = 0x0
dirarg = 0x555555fdeb80
dirsize = 1
ndir = 0
system_gdbinit = 0x0
home_gdbinit = 0x555556174960 "/home/efuller/.gdbinit"
local_gdbinit = 0x0
i = 0
save_auto_load = 1
objfile = 0x0
pre_stat_chain = 0x555555b2c000 <sentinel_cleanup>
(gdb) 

Следующая выдержка показывает gdb импортировать код Python и выполнить два поиска на основе значения локальной переменной. Первый поиск показывает несколько адресов, по которым это значение встречается (значение symarg а также execarg среди них). findbytes метод требует bytes объект, а не str объект. Второй поиск показывает только один адрес, который содержит адрес первого совпадения из первого поиска, который имеет имя в таблице символов.

(gdb) pi
>>> from structer import memmap, elf
>>> core = elf.Elf(memmap('core.17856'))
>>> from pprint import pprint
>>> 
(gdb) python pprint(tuple(hex(a) for a in core.findbytes(b"/home/efuller/gnu/bin/gdb")))
('0x555555fdef30',
 '0x55555606fce0',
 '0x55555614ff72',
 '0x5555562496a0',
 '0x55555624b915',
 '0x55555625f250',
 '0x5555562c6c4b',
 '0x55555689f2b5',
 '0x7ffff5f2d490',
 '0x7fffffffed74',
 '0x7fffffffed8e',
 '0x7fffffffedf0',
 '0x7fffffffefde')
(gdb) python pprint(tuple(hex(a) for a in core.findwords(0x555555fdef30)))
('0x555555faea38',)
(gdb) x/a 0x555555faea38
0x555555faea38 <_ZL16gdb_program_name>:     0x555555fdef30
(gdb) 

Следующий отрывок показывает другие варианты поиска. В поисках dirname из первого шаблона поиска выявляется несколько попаданий, которые включают в себя все совпадения из первого поиска. Последующий поиск отфильтровывает общие совпадения, требуя нулевого терминатора, а последующий поиск отфильтровывает попадания, которые не начинаются с нулевого терминатора. Эти последние два поиска показывают одинаковые результаты, хотя адреса различаются на единицу, потому что поиски, которые требуют начальную нулевую точку в этом начальном нулевом.

(gdb) python pprint(tuple(hex(a) for a in core.findbytes(b"/home/efuller/gnu/bin")))
('0x555555b4f701',
 '0x555555bd33f0',
 '0x555555fdef30',
 '0x55555606fce0',
 '0x55555614ff72',
 '0x5555562496a0',
 '0x55555624b915',
 '0x55555625f250',
 '0x5555562c6c4b',
 '0x55555689f2b5',
 '0x7ffff5f2d490',
 '0x7fffffffed74',
 '0x7fffffffed8e',
 '0x7fffffffedf0',
 '0x7fffffffefde')
(gdb) python pprint(tuple(hex(a) for a in core.findbytes(b"/home/efuller/gnu/bin\x00")))
('0x555555b4f701', '0x555555bd33f0')
(gdb) python pprint(tuple(hex(a) for a in core.findbytes(b"\x00/home/efuller/gnu/bin\x00")))
('0x555555b4f700', '0x555555bd33ef')
(gdb) 

Последний отрывок разделяет попадания из первого поиска на два случая: те, у которых нет начальных нулей, и те, у которых нет ведущих нулей. Последний использует наиболее общий тип поиска (тот, который оба findbytes а также findwords полагаться на), чтобы он мог включать ненулевые символы, предшествующие фиксированной части шаблона поиска.

(gdb) python pprint(tuple(hex(a) for a in core.findbytes(b"\x00/home/efuller/gnu/bin/gdb")))
('0x555555fdef2f',
 '0x55555606fcdf',
 '0x55555624969f',
 '0x55555625f24f',
 '0x7fffffffed73',
 '0x7fffffffed8d',
 '0x7fffffffefdd')
(gdb) python import re
(gdb) python pprint(tuple(hex(a) for a in core.find(re.compile(rb"\x00[^\x00]+/home/efuller/gnu/bin/gdb"))))
('0x55555614ff6f',
 '0x55555624b8ff',
 '0x5555562c6c37',
 '0x55555689f297',
 '0x7ffff5f2d487',
 '0x7fffffffeded')
(gdb) x/s 0x55555614ff6f + 1
0x55555614ff70:     "_=/home/efuller/gnu/bin/gdb"
(gdb) 

+ 1 в последней команде пропускается начальный ноль в этом поисковом запросе, хотя это также может быть включено в код поиска следующим образом.

(gdb) python pprint(tuple(hex(a+1) for a in core.find(re.compile(rb"\x00[^\x00]+/home/efuller/gnu/bin/gdb"))))
('0x55555614ff70',
 '0x55555624b900',
 '0x5555562c6c38',
 '0x55555689f298',
 '0x7ffff5f2d488',
 '0x7fffffffedee')
(gdb) 

Код структуры не требует gdb; он может работать в интерпретаторе Python вне GDB. Он не совместим с python2, поэтому запуск его внутри gdb требует gdb двоичный файл связан с python3.

Поиск шаблонов в основном файле может привести к результатам, которые не сообщаются методами поиска в коде структуры. Для этого есть две причины. Код Structer выполняет поиск только в сегментах загрузки, поэтому он не находит содержимое сегментов заметки, в котором содержатся различные элементы, которые не соответствуют виртуальным адресам в ядре. Код Structer не находит результатов, которые охватывают несколько сегментов нагрузки. Сообщение о таких результатах будет неправильным для двух соседних сегментов с пропуском (не нанесенная на карту область между сегментами). Код может объединять смежные сегменты, которые являются смежными, но он этого не делает, во всяком случае, пока.

Другие вопросы по тегам