Вывести адрес переменной python

Указатели в Python: в чем смысл?

Если вы когда-либо работали с языками более низкого уровня, такими как C или C ++, то вы, вероятно, слышали об указателях. Указатели позволяют создавать большую часть кода. Они также приводят в замешательство новичков и могут привести к различным ошибкам в управлении памятью, даже для экспертов. Итак, где они в Python, и как вы можете имитировать указатели в Python?

Указатели широко используются в C и C ++. По сути, они являются переменными, которые содержат адрес памяти другой переменной. Чтобы освежить в памяти указатели, вы можете рассмотреть этотoverview on C Pointers.

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

В этой статье вы:

  • Узнайте, почему не существует указателей в Python
  • Исследуйте разницу между переменными C и именами Python
  • Имитация указателей в Python
  • Поэкспериментируйте с реальными указателями, используя ctypes

Note: В этой статье «Python» будет относиться к эталонной реализации Python на C, также известной как CPython. Поскольку в статье обсуждаются некоторые внутренние особенности языка, эти примечания верны для CPython 3.7, но могут не соответствовать действительности в будущих или прошлых итерациях языка.

Free Bonus:Click here to get access to a chapter from Python Tricks: The Book, который демонстрирует вам лучшие практики Python на простых примерах, которые вы можете мгновенно применить для написания более красивого кода Pythonic.

Читайте также:  Java check main thread

Почему у Python нет указателей?

Правда в том, что я не знаю. Могут ли указатели в Python существовать изначально? Возможно, но указатели, похоже, противоречатZen of Python. Указатели поощряют неявные изменения, а не явные. Часто они сложны, а не просты, особенно для начинающих. Хуже того, они умоляют о способах выстрелить себе в ногу или сделать что-то действительно опасное, например прочитать из раздела памяти, который вы не должны были делать.

Python стремится абстрагироваться от деталей реализации, таких как адреса памяти, от своих пользователей. Python часто фокусируется на удобстве использования, а не на скорости. В результате, указатели в Python на самом деле не имеют смысла. Однако, чтобы не бояться, Python по умолчанию дает вам некоторые преимущества использования указателей.

Понимание указателей в Python требует краткого обзора деталей реализации Python. В частности, вы должны понимать:

Держитесь за свои адреса памяти, и давайте начнем.

Объекты в Python

В Python все является объектом. Для доказательства вы можете открыть REPL и исследовать с помощью isinstance() :

>>> isinstance(1, object) True >>> isinstance(list(), object) True >>> isinstance(True, object) True >>> def foo(): . pass . >>> isinstance(foo, object) True

Этот код показывает вам, что все в Python действительно является объектом. Каждый объект содержит как минимум три фрагмента данных:

reference count предназначен для управления памятью. Для более подробного изучения внутреннего устройства управления памятью в Python вы можете прочитатьMemory Management in Python.

Тип используется на уровне CPython для обеспечения безопасности типов во время выполнения. Наконец, есть значение, которое является фактическим значением, связанным с объектом.

Хотя не все объекты одинаковы. Есть еще одно важное различие, которое вам нужно понять: неизменяемые и изменяемые объекты. Понимание различий между типами объектов действительно помогает прояснить первый слой лука, который является указателем в Python.

Неизменные и изменчивые объекты

В Python есть два типа объектов:

  1. Immutable objects изменить нельзя.
  2. Mutable objects можно изменить.

Понимание этой разницы является первым ключом к навигации по указателям в Python. Вот разбивка общих типов и того, являются ли они изменяемыми или неизменяемыми:

Источник

Адрес переменной памяти среды печати Python

Возможно ли распечатать адрес памяти переменной окружения? С gdb-peda у меня есть адрес памяти, похожий на 0xbffffcd6 с searchmem , и я знаю его в правильной форме. ( 0xbfff. ), но gdb переместил стек с другой переменной среды. Я хотел бы с моим python script получить этот адрес, а затем сделать свой трюк и включить мой шеллкод. Я попробовал (с Python):

print hex(id(os.environ["ENVVAR"])) print memoryview(os.environ["ENVVAR"]) # output : # 0xb7b205c0L #

Мне было бы любопытно узнать больше о причине необходимости / делать это. В чем подвох? Какой шелл-код вы хотите включить? Зачем шелл-коду нужен адрес памяти переменной окружения. так далее

@ScottS. Спасибо за ваш интерес, это для простого эксплойта BSS переполнения, я знаю, что мог бы использовать другое решение, я нашел способ получить его в C, но гораздо проще получить доступ и работать с моим сценарием с python. Чтобы получить адрес переменной моей среды, соедините его с моим шелл-кодом и сделайте все, что я. Я начинаю смотреть код gdb-peda для поиска того, как он работает.

@eki-al eki-al Может быть загрузка libc с помощью ctypes и возможность вызова C getenv из Python. Смотрите документы по ctypes здесь: docs.python.org/2/library/ctypes.html

7 ответов

Функция cpython, встроенная в функцию id(), возвращает уникальный идентификатор для любого объекта, который не является именно этим адресом памяти, но находится как можно ближе к такому.

Например, мы имеем переменную x. id (x) не возвращает адрес памяти переменной x, а возвращает адрес памяти объекта, на который указывает x.

Там существует строгое разделение между «переменными» и «объектами памяти». В стандартной реализации python выделяет набор локалей и стек для виртуальной машины. Все локальные слоты не пересекаются, поэтому, если вы загружаете объект из локального слота x в стек и изменяете этот объект, «местоположение» слота x не изменяется.

Изображение 145043

http://docs.python.org/library/functions.html#id

Спасибо за это хорошее объяснение, но я думаю, что это может быть еще один способ доступа к адресу памяти, я быстро взглянул на peda github, для функции searchmem , как это обрабатывать, я протестирую в эти выходные: github.com/ longld / Пед / BLOB / .

Я полагаю, вы могли бы это сделать, используя ctypes модуль, чтобы напрямую вызвать нативный getenv :

import ctypes libc = ctypes.CDLL("libc.so.6") getenv = libc.getenv getenv.restype = ctypes.c_voidp print('%08x' % getenv('PATH')) 

Это кажется невыполнимой задачей, по крайней мере, в python. Из этого вопроса следует принять во внимание несколько факторов:

  • ASLR сделает это совершенно невозможным
  • Каждый двоичный файл может иметь собственные накладные расходы, разные argv, поэтому единственным надежным вариантом является выполнение двоичного файла и отслеживание его памяти до тех пор, пока мы не найдем переменную среды, которую мы ищем. В принципе, даже если мы сможем найти адрес окружения в процессе python, он будет находиться в другом месте в двоичном файле, который вы пытаетесь использовать.

Лучше всего ответить на этот вопрос — использовать http://python3-pwntools.readthedocs.io/en/latest/elf.html, который берет файл coredump, где легко найти адрес.

Спасибо, хороший ответ, просто откройте ASLR, но если у меня не установлен ASLR, он компилируется с флагом защиты стека?

Не уверен, чтобы понять ваш комментарий. ASLR — это защита всей системы, она не настроена в двоичном формате. Вы можете сделать cat /proc/sys/kernel/randomize_va_space чтобы увидеть, включен ли он (значение> 0). В противном случае, у вас также есть PIE-защита, которая может перетасовать размещение ENV в памяти. Никакой другой защиты не должно быть в других местах. Однако большинство двоичных файлов используют защиту NX, а стек помечен как неисполняемый, поэтому в этом случае вы не сможете поместить шелл-код в env.

ENV всегда в стеке в Linux. Структура стека выглядит следующим образом, начиная с самого высокого адреса: [зарезервированное пространство ядра (процесс не может его увидеть)] [ENV] [аргументы программы] [программный стек].

Имейте в виду, что переменная системной среды не является объектом, к которому вы можете получить доступ по адресу своей памяти. Каждый процесс, такой как Python или Ruby, запускающий ваш script, получит свою собственную копию среды. Вот почему результаты, возвращаемые интерпретаторами Python и Ruby, настолько различны.

Если вы хотите изменить переменную системной среды, вы должны использовать API, предоставляемый вашим языком программирования. См. этот или который для решения Python.

Спасибо за @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.

Функция getenv() по своей сути не является реентерабельной, поскольку возвращает значение, указывающее на статические данные.

Фактически, для повышения производительности getenv() реализация может также поддерживать отдельную копию среды в структуре данных, которую можно было бы искать гораздо быстрее (например, индексированная хеш-таблица или двоичное дерево) и обновить его и линейный список в среде при вызове setenv() или unsetenv().

Таким образом, адрес, возвращенный getenv, не обязательно из среды.

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 " (<>) 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.

Источник

Оцените статью