Адрес памяти переменной среды печати Python
Возможно ли распечатать адрес памяти переменной окружения?
С gdb-peda
у меня есть адрес памяти, похожий на 0xbffffcd6
с searchmem
, и я знаю его в правильной форме. (0xbfff????
), но gdb переместил стек с другой переменной среды.
Я хотел бы с моим python script получить этот адрес, а затем сделать свой трюк и включить мой шеллкод.
Я попробовал (с Python):
print hex(id(os.environ["ENVVAR"]))
print memoryview(os.environ["ENVVAR"])
# output :
# 0xb7b205c0L
# <memory at 0xb7b4dd9c>
С Ruby:
puts (ENV['PATH'].object_id << 1).to_s(16)
# output :
# -4836c38c
Если у кого-то есть идея, с python или ruby.
Ответы
Ответ 1
Функция cpython, встроенная в функцию id(), возвращает уникальный идентификатор для любого объекта, который не является именно этим адресом памяти, но находится как можно ближе к такому.
Например, мы имеем переменную x. id (x) не возвращает адрес памяти переменной x, а возвращает адрес памяти объекта, на который указывает x.
Там существует строгое разделение между "переменными" и "объектами памяти". В стандартной реализации python выделяет набор локалей и стек для виртуальной машины. Все локальные слоты не пересекаются, поэтому, если вы загружаете объект из локального слота x в стек и изменяете этот объект, "местоположение" слота x не изменяется.
http://docs.python.org/library/functions.html#id
Ответ 2
Это кажется невыполнимой задачей, по крайней мере, в python.
Из этого вопроса следует принять во внимание несколько факторов:
- ASLR сделает это совершенно невозможным
- Каждый двоичный файл может иметь собственные накладные расходы, разные argv, поэтому единственным надежным вариантом является выполнение двоичного файла и отслеживание его памяти до тех пор, пока мы не найдем переменную среды, которую мы ищем. В принципе, даже если мы сможем найти адрес окружения в процессе python, он будет находиться в другом месте в двоичном файле, который вы пытаетесь использовать.
Лучше всего ответить на этот вопрос - использовать http://python3-pwntools.readthedocs.io/en/latest/elf.html, который берет файл coredump, где легко найти адрес.
Ответ 3
Я полагаю, вы могли бы это сделать, используя ctypes
модуль, чтобы напрямую вызвать нативный getenv
:
import ctypes
libc = ctypes.CDLL("libc.so.6")
getenv = libc.getenv
getenv.restype = ctypes.c_voidp
print('%08x' % getenv('PATH'))
Ответ 4
Имейте в виду, что переменная системной среды не является объектом, к которому вы можете получить доступ по адресу своей памяти. Каждый процесс, такой как Python или Ruby, запускающий ваш script, получит свою собственную копию среды. Вот почему результаты, возвращаемые интерпретаторами Python и Ruby, настолько различны.
Если вы хотите изменить переменную системной среды, вы должны использовать API, предоставляемый вашим языком программирования.
См. этот или который для решения Python.
Ответ 5
Функция getenv() по своей сути не реентерабельна, потому что она возвращает значение, указывающее на статические данные.
Фактически, для более высокой производительности getenv() реализация также может поддерживать отдельную копию среды в структуре данных, которую можно искать гораздо быстрее (например, индексированную хеш-таблицу или двоичное дерево), и обновлять оба это и линейный список в среде, когда вызывается setenv() или unsetenv().
Таким образом, адрес, возвращаемый getenv, не обязательно должен быть из среды.
Расположение памяти процесса;
![]()
(источник: duartes.org)
![]()
(источник: cloudfront.net)
Карта памяти
import os
def mem_map():
path_hex = hex(id(os.getenv('PATH'))).rstrip('L')
path_address = int(path_hex, 16)
for line in open('/proc/self/maps'):
if 'stack' in line:
line = line.split()
first, second = line[0].split('-')
first, second = int(first, 16), int(second, 16)
#stack grows towards lower memory address
start, end = max(first, second), min(first, second)
print('stack:\n\tstart:\t0x{}\n\tend:\t0x{}\n\tsize:\t{}'.format(start, end, start - end))
if path_address in range(end, start+1):
print('\tgetenv("PATH") ({}) is in the stack'.format(path_hex))
else:
print('\tgetenv("PATH") ({}) is not in the stack'.format(path_hex))
if path_address > start:
print('\tgetenv("PATH") ({}) is above the stack'.format(path_hex))
else:
print('\tgetenv("PATH") ({}) is not above the stack'.format(path_hex))
print('')
continue
if 'heap' in line:
line = line.split()
first, second = line[0].split('-')
first, second = int(first, 16), int(second, 16)
#heap grows towards higher memory address
start, end = min(first, second), max(first, second)
print('heap:\n\tstart:\t0x{}\n\tend:\t0x{}\n\tsize:\t{}'.format(start, end, end - start))
if path_address in range(start, end+1):
print('\tgetenv("PATH") ({}) in the heap'.format(path_hex))
else:
print('\tgetenv("PATH") ({}) is not in the heap'.format(path_hex))
print('')
выход;
heap:
start: 0x170364928
end: 0x170930176
size: 565248
getenv("PATH") (0xb74d2330) is not in the heap
stack:
start: 0x0xbffa8000L
end: 0x0xbff86000L
size: 139264
getenv("PATH") (0xb74d2330) is not in the stack
getenv("PATH") (0xb74d2330) is not above the stack
Среда выше стека. Поэтому его адрес должен быть выше стека. Но адрес, который показывает id
, находится не в стеке, не в куче и не над стеком. Это действительно адрес? или мой расчет неверен!
Здесь код, чтобы проверить, где находится объект в памяти.
def where_in_mem(obj):
maps = {}
for line in open('/proc/self/maps'):
line = line.split()
start, end = line[0].split('-')
key = line[-1] if line[-1] != '0' else 'anonymous'
maps.setdefault(key, []).append((int(start, 16), int(end, 16)))
for key, pair in maps.items():
for start, end in pair:
# stack starts at higher memory address and grows towards lower memory address
if 'stack' in key:
if start >= id(obj) >= end:
print('Object "{}" ({}) in the range {} - {}, mapped to {}'.format(obj, hex(id(obj)), hex(start), hex(end), key))
continue
if start <= id(obj) <= end:
print('Object "{}" ({}) in the range {} - {}, mapped to {}'.format(obj, hex(id(obj)), hex(start), hex(end), key))
where_in_mem(1)
where_in_mem(os.getenv('PATH'))
выход;
Object "1" (0xa17f8b0) in the range 0xa173000 - 0xa1fd000, mapped to [heap]
Object "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" (0xb74a1330L) in the range 0xb7414000L - 0xb74d6000L, mapped to anonymous
Что анонимно в приведенном выше выводе?
Также можно создать отображение анонимной памяти, которое не соответствует никаким файлам, вместо этого оно используется для данных программы. В Linux, если вы запрашиваете большой блок памяти через malloc(), библиотека C создаст такое анонимное отображение вместо использования кучи памяти. ‘Большой означает больше байтов MMAP_THRESHOLD, 128 кБ по умолчанию и настраивается с помощью mallopt().
Анатомия программы в памяти
Таким образом, os.environ['PATH']
находится в malloc
ed области.
Ответ 6
Спасибо за @mickael9, я написал функцию для вычисления адреса переменной среды в программе:
def getEnvAddr(envName, ELFfile):
import ctypes
libc = ctypes.CDLL('libc.so.6')
getenv = libc.getenv
getenv.restype = ctypes.c_voidp
ptr = getenv(envName)
ptr += (len('/usr/bin/python') - len(ELFfile)) * 2
return ptr
Например:
[email protected]:~$ ./getenvaddr.elf PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
[email protected]:~$ python getenvaddr.py PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
[email protected]:~$
Примечание. Эта функция работает только в системе Linux.
Ответ 7
В рубине это возможно - этот пост охватывает общий случай:
Доступ к адресу памяти объектов в ruby ..? "Вы можете получить фактическое значение указателя объекта, взяв идентификатор объекта и выполнив побитовый сдвиг влево"
puts (ENV['RAILS_ENV'].object_id << 1).to_s(16)
> 7f84598a8d58