Hack The Virtual Memory: Python bytes
Hack虚拟内存之第1章:Python 字节对象
在这篇文章中,我们将完成与第0章(C字符串和/proc)几乎相同的事情,但我们将访问正在运行的Python 3脚本的虚拟内存。它不会(像第0章中)那么直接。
并在此过程中看看Python 3的一些内部结构!
前提
本文基于我们在前一章中学到的所有内容。在阅读本文之前,请阅读(并理解)第0章:C字符串和/proc。
为了完全理解本文,你需要知道:
C语言的基础知识
Python的基础知识
Linux文件系统和shell的基础知识
/proc文件系统的基础知识(参见第0章:C字符串和/proc,了解本主题的介绍)
环境
所有脚本和程序都已经在以下系统上进行过测试:
Ubuntu 14.04 LTSLinux ubuntu 4.4.0-31-generic #50~14.04.1-Ubuntu SMP Wed Jul 13 01:07:32 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
gccgcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
Python 3Python 3.4.3 (default, Nov 17 2016, 01:08:31)
[GCC 4.8.4] on linux
Python脚本
一开始我们将使用此脚本(main.py)并尝试在运行它的进程的虚拟内存中修改字符串“Holberton”。
#!/usr/bin/env python3
'''
Prints a b"string" (bytes object), reads a char from stdin
and prints the same (or not :)) string again
'''
import sys
s = b"Holberton"
print(s)
sys.stdin.read(1)
print(s)
关于字节对象
bytes vs str
如你所见,我们使用一个字节对象(我们在字符串前面使用‘b’前缀)来存储我们的字符串。此类型将以字节的形式存储字符串(与可能的多字节相比 - 你可以阅读unicodeobject.h以了解有关Python 3如何编码字符串的更多信息)。这可确保字符串在运行脚本的进程的虚拟内存中是一连串的ASCII码值。
实际上,s不是Python字符串(但在我们的上下文中并不重要):
julien@holberton:~/holberton/w/hackthevm1$ python3
Python 3.4.3 (default, Nov 17 2016, 01:08:31)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> s = "Betty"
>>> type(s)
>>> s = b"Betty"
>>> type(s)
>>> quit()
一切都是对象
Python中的所有东西都是一个对象:整数,字符串,字节,函数等等。因此,s = b“Holberton”应该创建一个字节类型的对象,并将字符串b“Holberton”存储在内存中。可能在堆中,因为它必须为对象所引用或存储的字节对象保留空间(此时我们不知道其确切的实现)。
对Python脚本运行read_write_heap.py
注意:read_write_heap.py是我们在上一章第0章:C字符串和/proc中写过的脚本
让我们运行上面的脚本,然后运行我们的read_write_heap.py脚本:
julien@holberton:~/holberton/w/hackthevm1$ ./main.py
b'Holberton'
此时,main.py正在等待用户输入。这对应于我们代码中的sys.stdin.read(1)。
运行read_write_heap.py:
julien@holberton:~/holberton/w/hackthevm1$ ps aux | grep main.py | grep -v grep
julien 3929 0.0 0.7 31412 7848 pts/0 S+ 15:10 0:00 python3 ./main.py
julien@holberton:~/holberton/w/hackthevm1$ sudo ./read_write_heap.py 3929 Holberton "~ Betty ~"
[*] maps: /proc/3929/maps
[*] mem: /proc/3929/mem
[*] Found [heap]:
pathname = [heap]
addresses = 022dc000-023c6000
permisions = rw-p
offset = 00000000
inode = 0
Addr start [22dc000] | end [23c6000]
[*] Found 'Holberton' at 8e192
[*] Writing '~ Betty ~' at 236a192
julien@holberton:~/holberton/w/hackthevm1$
正如所料,我们在堆上找到了字符串(“Holberton”)并替换了它。现在,当我们在main.py脚本中按下Enter键时,它将打印b'~Betty~':
b'Holberton'
julien@holberton:~/holberton/w/hackthevm1$
等一下
我们找到字符串“Holberton”并替换它,但并没有输出正确的字符串?
在我们深入思考前,还有一件事需要检查。我们的脚本在找到第一个字符串后停止。让我们运行几次,看看堆中是否有更多相同的字符串。
julien@holberton:~/holberton/w/hackthevm1$ ./main.py
b'Holberton'
julien@holberton:~/holberton/w/hackthevm1$ ps aux | grep main.py | grep -v grep
julien 4051 0.1 0.7 31412 7832 pts/0 S+ 15:53 0:00 python3 ./main.py
julien@holberton:~/holberton/w/hackthevm1$ sudo ./read_write_heap.py 4051 Holberton "~ Betty ~"
[*] maps: /proc/4051/maps
[*] mem: /proc/4051/mem
[*] Found [heap]:
pathname = [heap]
addresses = 00bf4000-00cde000
permisions = rw-p
offset = 00000000
inode = 0
Addr start [bf4000] | end [cde000]
[*] Found 'Holberton' at 8e162
[*] Writing '~ Betty ~' at c82162
julien@holberton:~/holberton/w/hackthevm1$ sudo ./read_write_heap.py 4051 Holberton "~ Betty ~"
[*] maps: /proc/4051/maps
[*] mem: /proc/4051/mem
[*] Found [heap]:
pathname = [heap]
addresses = 00bf4000-00cde000
permisions = rw-p
offset = 00000000
inode = 0
Addr start [bf4000] | end [cde000]
Can't find 'Holberton'
julien@holberton:~/holberton/w/hackthevm1$
只出现一次。那么脚本中使用的字符串“Holberton”在哪里?Python字节对象在内存中的哪个位置?它可能在栈中吗?在read_write_heap.py脚本中用“[stack]”来替换“[heap]”,创建read_write_stack.py:
(*)参见上一篇文章,栈在/proc/[pid]/maps文件中被称为“[stack]”
#!/usr/bin/env python3
'''
Locates and replaces the first occurrence of a string in the stack
of a process
Usage: ./read_write_stack.py PID search_string replace_by_string
Where:
- PID is the pid of the target process
- search_string is the ASCII string you are looking to overwrite
- replace_by_string is the ASCII string you want to replace
search_string with
'''
import sys
def print_usage_and_exit():
print('Usage: {} pid search write'.format(sys.argv[0]))
sys.exit(1)
# check usage
if len(sys.argv) != 4:
print_usage_and_exit()
# get the pid from args
pid = int(sys.argv[1])
if pid <= 0:
print_usage_and_exit()
search_string = str(sys.argv[2])
if search_string == "":
print_usage_and_exit()
write_string = str(sys.argv[3])
if search_string == "":
print_usage_and_exit()
# open the maps and mem files of the process
maps_filename = "/proc/{}/maps".format(pid)
print("[*] maps: {}".format(maps_filename))
mem_filename = "/proc/{}/mem".format(pid)
print("[*] mem: {}".format(mem_filename))
# try opening the maps file
try:
maps_file = open('/proc/{}/maps'.format(pi