Python打印环境变量内存地址

时间:2021-12-24 11:22:14

Is it possible to print my environment variable memory address ?

是否可以打印我的环境变量内存地址?

With gdb-peda i have a memory address looking like 0xbffffcd6 with searchmem and i know it's the right form. (0xbfff????) but gdb moved the stack with some other environment variable.

使用gdb-peda我的内存地址看起来像是带有searchmem的0xbffffcd6,我知道它是正确的形式。 (0xbfff ????)但是gdb用其他一些环境变量移动了堆栈。

I would like with my python script to get this address and then do my trick and include my shellcode.

我想用我的python脚本来获取这个地址然后做我的技巧并包含我的shellcode。

i tried (with Python):

我试过(使用Python):

print hex(id(os.environ["ENVVAR"]))
print memoryview(os.environ["ENVVAR"])

# output :
# 0xb7b205c0L
# <memory at 0xb7b4dd9c>

With Ruby :

使用Ruby:

puts (ENV['PATH'].object_id << 1).to_s(16)
# output :
# -4836c38c

If anyone have an idea, with python or ruby.

如果有人有想法,使用python或ruby。

7 个解决方案

#1


4  

The cpython built in function id() returns a unique id for any object, which is not exactly it's memory address but is as close as you can get to such.

内置函数id()的cpython为任何对象返回一个唯一的id,这不是它的内存地址,而是尽可能接近这样。

For example, we have variable x. id(x) does not return the memory address of the variable x, rather it returns the memory address of the object that x points to.

例如,我们有变量x。 id(x)不返回变量x的内存地址,而是返回x指向的对象的内存地址。

There's a strict separation between 'variables' and 'memory objects'. In the standard implementation, python allocates a set of locals and a stack for the virtual machine to operate on. All local slots are disjoint, so if you load an object from local slot x onto the stack and modify that object, the "location" of the x slot doesn't change.

“变量”和“记忆对象”之间存在严格的分离。在标准实现中,python为虚拟机分配一组本地和一个堆栈来进行操作。所有本地插槽都是不相交的,因此如果将对象从本地插槽x加载到堆栈并修改该对象,则x插槽的“位置”不会更改。

Python打印环境变量内存地址 http://docs.python.org/library/functions.html#id

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

#2


3  

This seems an impossible task at least in python. There are few things to take in consideration from this question:

至少在python中,这似乎是一项不可能完成的任务。从这个问题中可以考虑的事情很少:

  • ASLR would make this completely impossible
  • ASLR会让这完全不可能
  • Every binary can have it's own overhead, different argv, so, the only reliable option is to execute the binary and trace it's memory until we found the environment variable we are looking for. Basically, even if we can find the environment address in the python process, it would be at a different position in the binary you are trying to exploit.
  • 每个二进制文件都有它自己的开销,不同的argv,因此,唯一可靠的选择是执行二进制文件并跟踪它的内存,直到找到我们正在寻找的环境变量。基本上,即使我们可以在python进程中找到环境地址,它也会在您尝试利用的二进制文件中处于不同的位置。

Best fit to answer this question is to use http://python3-pwntools.readthedocs.io/en/latest/elf.html which is taking a coredump file where it's easy to find the address.

最适合回答这个问题的是使用http://python3-pwntools.readthedocs.io/en/latest/elf.html这是一个coredump文件,很容易找到地址。

#3


3  

I suppose you could do that using the ctypes module to call the native getenv directly :

我想你可以使用ctypes模块直接调用本机getenv:

import ctypes

libc = ctypes.CDLL("libc.so.6")

getenv = libc.getenv
getenv.restype = ctypes.c_voidp

print('%08x' % getenv('PATH'))

#4


2  

Please keep in mind that system environment variable is not an object you can access by its memory address. Each process, like Python or Ruby process running your script will receive its own copy of environment. Thats why results returned by Python and Ruby interpreters are so different.

请记住,系统环境变量不是您可以通过其内存地址访问的对象。每个进程(如运行脚本的Python或Ruby进程)都将收到自己的环境副本。这就是Python和Ruby解释器返回的结果如此不同的原因。

If you would like to modify system environment variable you should use API provided by your programming language. Please see this or that post for Python solution.

如果要修改系统环境变量,则应使用编程语言提供的API。请查看Python或该解决方案的帖子。

#5


1  

The getenv() function is inherently not reentrant because it returns a value pointing to static data.

getenv()函数本质上不可重入,因为它返回指向静态数据的值。

In fact, for higher performance of getenv(), the implementation could also maintain a separate copy of the environment in a data structure that could be searched much more quickly (such as an indexed hash table, or a binary tree), and update both it and the linear list at environ when setenv() or unsetenv() is invoked.

实际上,为了提高getenv()的性能,实现还可以在数据结构中维护单独的环境副本,以便更快地搜索(例如索引哈希表或二叉树),并更新这两者当调用setenv()或unsetenv()时,它和environ中的线性列表。

So the address returned by getenv is not necessarily from the environment.

因此getenv返回的地址不一定来自环境。

Process memory layout;

进程内存布局;

http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png

http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/Memory-Layout-300x255.gif

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/Memory-Layout-300x255.gif

Memory map

记忆地图

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('')

Output;

输出;

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

Environment is above the stack. So its address should be higher than the stack. But the address id shows is not in the stack, not in the heap and not above the stack. Is it really an address? or my calculation is wrong!

环境在堆栈之上。所以它的地址应该高于堆栈。但地址id显示不在堆栈中,不在堆中而不在堆栈上方。它真的是一个地址吗?或者我的计算错了!

Here's the code to check where an object lies in memory.

这是检查对象在内存中的位置的代码。

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'))

Output;

输出;

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

What's anonymous in the above output?

在上面的输出中有什么匿名?

It is also possible to create an anonymous memory mapping that does not correspond to any files, being used instead for program data. In Linux, if you request a large block of memory via malloc(), the C library will create such an anonymous mapping instead of using heap memory. ‘Large’ means larger than MMAP_THRESHOLD bytes, 128 kB by default and adjustable via mallopt().

还可以创建与任何文件不对应的匿名内存映射,而不是用于程序数据。在Linux中,如果通过malloc()请求大块内存,C库将创建这样的匿名映射,而不是使用堆内存。 “大”意味着大于MMAP_THRESHOLD字节,默认为128 kB,可通过mallopt()调整。

Anatomy of a Program in Memory

内存中程序的剖析

So the os.environ['PATH'] is in the malloced region.

因此os.environ ['PATH']位于malloced区域。

#6


1  

Thanks for @mickael9, I have writen a function to calculate address of an environment variable in a program:

感谢@ 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

For example:

例如:

user@host:~$ ./getenvaddr.elf PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
user@host:~$ python getenvaddr.py PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
user@host:~$

Note: This function only works in Linux system.

注意:此功能仅适用于Linux系统。

#7


-1  

In ruby it's possible - this post covers the general case: Accessing objects memory address in ruby..? "You can get the actual pointer value of an object by taking the object id, and doing a bitwise shift to the left"

在ruby中它是可能的 - 这篇文章涵盖了一般情况:在ruby中访问对象的内存地址..? “你可以通过获取对象id来获取对象的实际指针值,然后按位向左移动”

puts (ENV['RAILS_ENV'].object_id << 1).to_s(16)
> 7f84598a8d58

#1


4  

The cpython built in function id() returns a unique id for any object, which is not exactly it's memory address but is as close as you can get to such.

内置函数id()的cpython为任何对象返回一个唯一的id,这不是它的内存地址,而是尽可能接近这样。

For example, we have variable x. id(x) does not return the memory address of the variable x, rather it returns the memory address of the object that x points to.

例如,我们有变量x。 id(x)不返回变量x的内存地址,而是返回x指向的对象的内存地址。

There's a strict separation between 'variables' and 'memory objects'. In the standard implementation, python allocates a set of locals and a stack for the virtual machine to operate on. All local slots are disjoint, so if you load an object from local slot x onto the stack and modify that object, the "location" of the x slot doesn't change.

“变量”和“记忆对象”之间存在严格的分离。在标准实现中,python为虚拟机分配一组本地和一个堆栈来进行操作。所有本地插槽都是不相交的,因此如果将对象从本地插槽x加载到堆栈并修改该对象,则x插槽的“位置”不会更改。

Python打印环境变量内存地址 http://docs.python.org/library/functions.html#id

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

#2


3  

This seems an impossible task at least in python. There are few things to take in consideration from this question:

至少在python中,这似乎是一项不可能完成的任务。从这个问题中可以考虑的事情很少:

  • ASLR would make this completely impossible
  • ASLR会让这完全不可能
  • Every binary can have it's own overhead, different argv, so, the only reliable option is to execute the binary and trace it's memory until we found the environment variable we are looking for. Basically, even if we can find the environment address in the python process, it would be at a different position in the binary you are trying to exploit.
  • 每个二进制文件都有它自己的开销,不同的argv,因此,唯一可靠的选择是执行二进制文件并跟踪它的内存,直到找到我们正在寻找的环境变量。基本上,即使我们可以在python进程中找到环境地址,它也会在您尝试利用的二进制文件中处于不同的位置。

Best fit to answer this question is to use http://python3-pwntools.readthedocs.io/en/latest/elf.html which is taking a coredump file where it's easy to find the address.

最适合回答这个问题的是使用http://python3-pwntools.readthedocs.io/en/latest/elf.html这是一个coredump文件,很容易找到地址。

#3


3  

I suppose you could do that using the ctypes module to call the native getenv directly :

我想你可以使用ctypes模块直接调用本机getenv:

import ctypes

libc = ctypes.CDLL("libc.so.6")

getenv = libc.getenv
getenv.restype = ctypes.c_voidp

print('%08x' % getenv('PATH'))

#4


2  

Please keep in mind that system environment variable is not an object you can access by its memory address. Each process, like Python or Ruby process running your script will receive its own copy of environment. Thats why results returned by Python and Ruby interpreters are so different.

请记住,系统环境变量不是您可以通过其内存地址访问的对象。每个进程(如运行脚本的Python或Ruby进程)都将收到自己的环境副本。这就是Python和Ruby解释器返回的结果如此不同的原因。

If you would like to modify system environment variable you should use API provided by your programming language. Please see this or that post for Python solution.

如果要修改系统环境变量,则应使用编程语言提供的API。请查看Python或该解决方案的帖子。

#5


1  

The getenv() function is inherently not reentrant because it returns a value pointing to static data.

getenv()函数本质上不可重入,因为它返回指向静态数据的值。

In fact, for higher performance of getenv(), the implementation could also maintain a separate copy of the environment in a data structure that could be searched much more quickly (such as an indexed hash table, or a binary tree), and update both it and the linear list at environ when setenv() or unsetenv() is invoked.

实际上,为了提高getenv()的性能,实现还可以在数据结构中维护单独的环境副本,以便更快地搜索(例如索引哈希表或二叉树),并更新这两者当调用setenv()或unsetenv()时,它和environ中的线性列表。

So the address returned by getenv is not necessarily from the environment.

因此getenv返回的地址不一定来自环境。

Process memory layout;

进程内存布局;

http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png

http://static.duartes.org/img/blogPosts/linuxFlexibleAddressSpaceLayout.png

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/Memory-Layout-300x255.gif

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/Memory-Layout-300x255.gif

Memory map

记忆地图

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('')

Output;

输出;

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

Environment is above the stack. So its address should be higher than the stack. But the address id shows is not in the stack, not in the heap and not above the stack. Is it really an address? or my calculation is wrong!

环境在堆栈之上。所以它的地址应该高于堆栈。但地址id显示不在堆栈中,不在堆中而不在堆栈上方。它真的是一个地址吗?或者我的计算错了!

Here's the code to check where an object lies in memory.

这是检查对象在内存中的位置的代码。

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'))

Output;

输出;

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

What's anonymous in the above output?

在上面的输出中有什么匿名?

It is also possible to create an anonymous memory mapping that does not correspond to any files, being used instead for program data. In Linux, if you request a large block of memory via malloc(), the C library will create such an anonymous mapping instead of using heap memory. ‘Large’ means larger than MMAP_THRESHOLD bytes, 128 kB by default and adjustable via mallopt().

还可以创建与任何文件不对应的匿名内存映射,而不是用于程序数据。在Linux中,如果通过malloc()请求大块内存,C库将创建这样的匿名映射,而不是使用堆内存。 “大”意味着大于MMAP_THRESHOLD字节,默认为128 kB,可通过mallopt()调整。

Anatomy of a Program in Memory

内存中程序的剖析

So the os.environ['PATH'] is in the malloced region.

因此os.environ ['PATH']位于malloced区域。

#6


1  

Thanks for @mickael9, I have writen a function to calculate address of an environment variable in a program:

感谢@ 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

For example:

例如:

user@host:~$ ./getenvaddr.elf PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
user@host:~$ python getenvaddr.py PATH /bin/ls
PATH will be at 0xbfffff22 in /bin/ls
user@host:~$

Note: This function only works in Linux system.

注意:此功能仅适用于Linux系统。

#7


-1  

In ruby it's possible - this post covers the general case: Accessing objects memory address in ruby..? "You can get the actual pointer value of an object by taking the object id, and doing a bitwise shift to the left"

在ruby中它是可能的 - 这篇文章涵盖了一般情况:在ruby中访问对象的内存地址..? “你可以通过获取对象id来获取对象的实际指针值,然后按位向左移动”

puts (ENV['RAILS_ENV'].object_id << 1).to_s(16)
> 7f84598a8d58