第七题 input
这道题可太痛苦了,好多知识点;
这里只需要按它的比较走就可以了。
解题过程
第一次比较:
这里只需要让输入的长度为100,并且满足argv[‘A’]、[‘B’]的值即可
答案如下:
代码如下:
#第一个参数应该是调用的函数名称
args.append('/home/input2/input')
#构建第一次判断答案
for i in range(1,100):
args.append("a")
args[ord('A')]=""
args[ord('B')]="\x20\x0a\x0d"
第二次比较
read的第一个参数为fd(文件描述符),表示输入来源,可以为0、1、2,分别表示标准输入,标准输出和标准错误,输入分别来自于键盘输入,显示器,显示器。
这里可以使用os.write(fd, str)函数来进行写入,其中fd为文件描述符,str为输入的字符串,构建代码如下:
os.write(0,"\x00\x0a\x00\xff")
os.write(2,"\x00\x0a\x02\xff")
第三次比较
getenv函数格式如下:
char *getenv(const char *name)
表示读取name所在环境的值,因此只需要将\xde\xad\xbe\xef地址处存放的数据进行赋值即可,代码如下:
# 第三次判断答案
environ={"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}
第四个比较
这里只需要打开一个文件进行写入即可:
fp=open("\x0a","wb") #wb表示只写一个二进制文件
fp.write("\x00"*4)
fp.close()
socket函数
在讲第五次比较前先说一下socket函数
该函数的使用格式如下:
socket.socket(socket_family,socket_type,protocal=0)
其中socket_family 可以是 AF_UNIX 或 AF_INET。AF_UNIX用于同一台机器上的进程间通信,AF_INET用于IPV4协议的TCP和UDP。
socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。分别表示流套接字和数据报文套接字。
流式套接字
使用这种套接字时,数据在客户端是顺序发送的,并且到达的顺序是一致的。比如你在客户端先发送1,再发送2,那么在服务器端的接收顺序是先接收到1,再接收到2,流式套接字是可靠的,是面向连接的;
数据报套接字
这种套接字是无连接的,数据是打包成数据包发送的,到达的顺序不一定与发送的顺序是一致的,并且数据不一定是可达的,并且接收到的数据还可能出错。
protocol 一般不填,默认值为 0。
第五个比较
这里直接使用socket端口连接传递数据即可,注意sd.connect指定的端口因和argc[‘C’]赋的值相同,这样才能正常连接并传递数据,这里设置为8888,构造答案如下:
args[ord('C')]="8888"
sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
time.sleep(2)#因为socket连接需要一点时间,如果没有等待很可能连接错误,没有这句话的话就得多运行一遍就好了
sd.connect(("127.0.0.1",8888))
sd.send("\xde\xad\xbe\xef")
sd.close()
答案
1、 在tmp目录下创建一个python文件并写入下面代码:
import socket
import os
import subprocess
import time
stdinr,stdinw=os.pipe()
stderr,stderw=os.pipe()
args=[]
args.append('/home/input2/input')
#构建第一次判断答案
for i in range(1,100):
args.append("a")
args[ord('A')]=""
args[ord('B')]="\x20\x0a\x0d"
args[ord('C')]="8888" #这里的端口和sd链接的端口应该一样
#第二次判断答案
os.write(stdinw,"\x00\x0a\x00\xff")
os.write(stderw,"\x00\x0a\x02\xff")
# 第三次判断答案
environ={"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}
#传递参数
pro=subprocess.Popen(args,stdin=stdinr,stderr=stderr,env=environ)
fp=open("\x0a","wb") #wb表示只写一个二进制文件
fp.write("\x00"*4)
fp.close()
sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0);
time.sleep(2)#因为socket连接需要一点时间,如果没有等待很可能连接错误,没有这句话的话就得多运行一遍就好了
sd.connect(("127.0.0.1",8888))
sd.send("\xde\xad\xbe\xef")
sd.close()
- 将flag进行软链接到tmp目录下:
ln -s /home/input2/flag flag
- 执行python文件
过程如下:
需要先在tmp目录下自行创建一个目录并在该目录下入python文件
这里不知道为什么添加注释会报错,将全部注释删除就可以了。
函数讲解
ln -s指令
这个python文件需要放在/tmp文件下,由于flag这里写的是相对地址,因此需要将其进行软链接到tmp目录下。
ln -s /home/input2/flag flag
ln命令 用来为文件创建链接,链接类型分为硬链接和符号链接两种,默认的链接类型是硬链接。如果要创建符号链接必须使用"-s"选项
硬链接的意思是一个档案可以有多个名称,而软链接的方式则是产生一个特殊的档案,该档案的内容是指向另一个档案的位置。硬链接是存在同一个文件系统中,而软链接却可以跨越不同的文件系统
格式:ln [参数][源文件或目录][目标文件或目录]
例如这里用到的指令:ln -s /home/input2/flag flag
更详细的信息可以点击这个链接
其他操作:
查看建立的链接
ls -l
删除软链接
rm -rf flag
subprocess模块
该模块的popen函数用于查看用户的输入,其基本格式如下:
subprocess.Popen(‘命令’, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
其中shell=true可省略,stdout=subprocess.PIPE表示当命令存在的时候,把结果写入到stdout管道
stderr=sbuprocess.PIPE 表示当命令不存在的时候,把结果吸入到stderr管道
env表示给环境变量赋值,对应与getenv,获取环境变量值
ord函数
该函数是python中的一个将字符(一个字符)转换为int类型数据的函数
os.pipe()
用于创建管道并将信息从一个进程传递给另一个进程,它有一对文件描述符,r和w,一个用于读取数据,一个用于写入数据的,因此在使用时也是成对使用:
stdinr, stdinw = os.pipe()
#写入
os.write(stdinw, "\x00\x0a\x00\xff")
#而要使用写入的值则使用stdinr
TypeError: a bytes-like object is required, not ‘str’
然后在本地运行的时候还报了这个错,而在ssh的链接上则能正常运行。
主要是pwnable的终端用的是python2,而本地我用的python3,而python3对数据类型进行了更细的划分,在这里把数据当成了str类型,而实际上这里传输的是二进制的数据,因此在字符串前加一个b就可以了。
os.write(stdinw,b"\x00\x0a\x00\xff")
第八题leg
知识点记录
- pc寄存器用于存储当前欲执行指令的地址,也就是当前正在执行的指令的下一条指令
- bx跳转
这个可以参看此链接
B、BL、BX、BLX 和 BXJ:
分别对应跳转、带链接跳转(带返回的跳转)、跳转并切换指令集、带链接跳转并切换指令集(带返回的跳转并切换指令集)、跳转并转换到 Jazelle 状态
此处的bx表示跳转并切换指令集,根据地址的最低位确定是否状态切换。如果末尾是1则切换到thumb状态,否则保留在asm状态,而需要注意的是在thumb中pc到下一条地址只需要+4,在asm中则是+8
3. lr寄存器
该寄存器有两个用途,一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2)。
同时,当通过BL或BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回
解题过程
打开leg.asm可以看到key1、key2、key3都是获取的r0来进行的计算,因此只需要查看对应函数的r0是怎么得到的即可
-
首先是key1,可以看到它的结果是pc,而pc的值应该是当前执行指令的下一条指令,在ARM汇编指令中,寄存器pc的值为当前指令地址加8个字节,也就是key1=0x00008cdc+8
-
接下来看key2
红色方框圈出了计算过程,r6=pc+1=0x00008cfc+1,然后对r6执行bx切换,由于r6的二进制为:
末位为1会切换到thumb状态,在thumb状态下寄存器的值为当前指令加4,因此r3=0x8d04+4,执行adds后r3再次+4,因此r3=0x8d04+4+4=0x8d0c
3. 接下来是key3
前面已经提到lr指令存放的是子程序的返回地址,也就是当前指令执行完成后子程序回去继续执行的地方,因此去main中找,也就是0x8d80=key3
- 总结
通过以上过程我已经知道了key1、2、3,因此直接就可以计算出结果了
key=key1+key2+key3=0x00008cdc+8+0x8d0c+0x8d80=0x1A772=108400