首先看一下题目
题目的意思是让你玩一个游戏,给你一些金币,其中有一个假币,这个假币外表和真的金币一样,但是重量不同,真的金币重量为10
,假币重量为9
。你需要帮他找到100
枚假币才能过关。
游戏规矩如下:
- 给你
N
枚硬币和C
次机会 - 选择硬币的序号,称重
- 你获得硬币的重量
第二步
和第三步
重复C
次,然后你给出正确答案(也就是哪一枚是假币)
下面给出了一个列子,比如给你4枚
硬币,2次
机会,你第一次选择称重序号为0,1
的硬币,返回给你他们的重量为20
(也就是0,1
这两枚硬币为真的),然后重复第二步第三步,这时你选择了序号为3
的硬币去称重,返回结果为10
,说明序号为2
的硬币为假的(注意题干,真硬币重量为10
,假硬币重量为9
),然后你将假币的序号发送给他,他会返回给你“Correct!”
。
这道题没有涉及到漏洞方面的知识,就是锻炼你用pwntools
去交互,以及使用了二分查找
这个算法。例子已经隐约的给出提示用二分法
了,二分查找
也就折半查找
,是一种效率较高的查找方法,他查找的次数最多为
⌈
l
o
g
2
(
n
+
1
)
⌉
\lceil log_2(n+1)\rceil
⌈log2(n+1)⌉
对于二分查找
不太了解的可以去百度搜索一下相关资料,下面是这道题的脚本
from pwn import *
import re
#将日志等级设置为debug,为了将交互的消息打印到屏幕,方便调试
context.log_level = 'debug'
conn = remote('pwnable.kr', 9007)
conn.recvuntil("Ready?")
#匹配模式,匹配N和C的值
start_match = """N=(\d+) C=(\d+)"""
while True:
line = conn.recvline_regex(start_match)
result = re.search(start_match, line)
n,c = result.groups()
n = int(n)
c = int(c)
print n,c
#设置初始区间
start = 0
end = n-1
for i in xrange(c):
#取区间中间点
mid = (start + end) / 2
print "Round", 1, start, mid, end
#将(start,mid+1)输入到服务器
coins_to_test = [str(i) for i in xrange(start, mid+1)]
output = " ".join(coins_to_test)
conn.sendline(output)
#匹配返回值(重量)
response_match = """(\d+)"""
line = conn.recvline()
result = re.search(response_match, line)
weight = int(result.groups()[0])
expected_weight = len(coins_to_test) * 10
print expected_weight, weight
#检测返回结果,更新区间
if weight == expected_weight:
start = mid + 1
end = end
else:
start = start
end = mid
#最后给出我们的选择(哪一个是假币)
conn.sendline(str(start))
conn.recvuntil("Correct!")
运行一下,提示超时报错,应该是网络延迟造成的,这里我们用之前的账号登录进去,在/tmp
目录下创建个自己的目录,将脚本传入这个目录,在本地执行脚本。既然在本机执行,那么连接的语句(conn = remote('pwnable.kr', 9007)
)需要修改一下,改成conn = remote('127.0.0.1', 9007)
。这里我用的是fd
的账号登录的,在/tmp
下创建了cusuall
目录,将脚本传进去跑一下就出结果了。