关于ruby:Python打印环境变量的内存地址

Python print environment variable memory address

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

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

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

我试过(用Python):

1
2
3
4
5
6
print hex(id(os.environ["ENVVAR"]))
print memoryview(os.environ["ENVVAR"])

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

与露比:

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

如果有人有主意,用python或ruby。


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

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

"变量"和"内存对象"之间有严格的分隔。在标准实现中,python为虚拟机分配一组本地变量和一个堆栈以供操作。所有本地槽都是不相交的,因此如果将对象从本地槽X加载到堆栈并修改该对象,则X槽的"位置"不会更改。

enter image description herehttp://docs.python.org/library/functions.html_id


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

1
2
3
4
5
6
7
8
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文件中很容易找到地址。


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

如果要修改系统环境变量,应使用编程语言提供的API。请看这个或那个关于python解决方案的帖子。


感谢@mickal9,我编写了一个函数来计算程序中环境变量的地址:

1
2
3
4
5
6
7
8
9
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

例如:

1
2
3
4
5
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:~$

注意:此功能仅在Linux系统中工作。


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

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返回的地址不一定来自环境。

进程内存布局;

http://static.duartes.org/img/blogposts/linuxflexibleddressspacelayout.png

http://d1gjlxt8vb0knt.cloudfront.net//wp-content/uploads/memory-layout-300x255.gif

内存映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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:
\tstart:\t0x{}
\tend:\t0x{}
\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:
\tstart:\t0x{}
\tend:\t0x{}
\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('')

输出;

1
2
3
4
5
6
7
8
9
10
11
12
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显示的地址不在堆栈中,不在堆栈中,也不在堆栈上方。它真的是地址吗?不然我算错了!

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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'))

输出;

1
2
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

上面的输出中匿名的是什么?

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

记忆中程序的解剖

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


在Ruby中,这是可能的——这篇文章涵盖了一般情况:访问Ruby中的对象内存地址….?"通过获取对象ID并向左按位移动,可以获得对象的实际指针值。"

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