-
题目分类:general
-
题目分值:200
你能在狗狗银行成功薅到羊毛吗?
公告 1:本题前端计算存在浮点数导致的计算误差,数字特别极端时显示可能不正确。但后端采用大整数精确计算,只有净资产确实高于 2000 时才会给出 flag。
公告 2:本题增加两个备用服务器:备用服务器 1、备用服务器 2。不同服务器之间数据不互通。
公告 3:本题新增限制:每个用户最多 1000 张卡,每张卡最多 100000 条交易。已经超过此限制的用户的数据已被重置。由于我们正在处理原始服务器的数据,请选手暂时使用备用服务器做题,处理完成后我们会发布新的通知。
公告 4:原始服务器已经恢复正常运行,选手可以通过下面的打开题目按钮使用。备用服务器也会正常运行到比赛结束。组委会特别提醒:选手在做题过程中不应该恶意扫描爆破、恶意占用资源等。对于违反比赛规则的选手,组委会将取消其参赛资格并予以公示。
题目源代码与解题脚本见附件,其中 solution.py
为解题脚本。
这道题的主要考点是利用利息四舍五入的规则将实际到手利率翻倍,次要考点是编写脚本自动执行重复性操作。
利率翻倍这一步没什么好说的,简单实验一会就不难发现,当储蓄卡存有 167 狗狗币时,每日利息应当是 0.501 狗狗币,但四舍五入就是 1 狗狗币,实际上利率约等于 0.6%,超过了信用卡利率。信用卡没有这个漏洞,不会因为利息不足 0.5 狗狗币就变成 0,这是有意设计的,避免开很多张信用卡就可以实现零利息获得任意数量的钱。
一个简单的(但不是最节约操作次数的)策略是:开一张信用卡,借很多钱,分开存到很多张储蓄卡中,每张卡存恰好 167 狗狗币。这样一来,这笔钱每天净赚 0.1% 利息,逐渐就能积累到 1000 狗狗币。每隔几天,应当把所有储蓄卡中获得的利息拿走,还回信用卡,因为每张储蓄卡存恰好 167 狗狗币时赚钱最快,越多越慢,当每张储蓄卡存 200 狗狗币时会恰好无法赚钱,之后会开始亏钱。附件中的解题脚本就是按照这个思路编写的,开了 200 张储蓄卡,每天都拿走利息,37 天可以获胜。
以下以最新版 Chrome 浏览器为例,其他主流浏览器一般都有类似功能。提到的浏览器界面上的文字都是英文版,使用中文版的请自行猜测我在说什么。(或者干脆别看这一段了,直接去网上搜搜怎么用 Python 写爬虫发请求应该就够了。)
要想编写脚本自动操作,首先需要知道在浏览器中手工操作时会产生什么样的网络请求。如果通过脚本发送一模一样的网络请求,就能取代手工操作,产生一模一样的效果。在狗狗银行页面上按 F12 键打开“开发者工具”,在其中切换到“Network”页面,就可以看到所有浏览器发送的网络请求。去办储蓄卡,会发现每当点击“办卡”按钮时就会有一个“create”请求出现,它的 Url 是 http://202.38.93.111:10100/api/create
。在这一行上点右键,“Copy”,“Copy as cURL”,就能复制出类似于这样的命令:
curl 'http://202.38.93.111:10100/api/create' \
-H 'Connection: keep-alive' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'Accept: application/json, text/plain, */*' \
-H 'DNT: 1' \
-H 'Authorization: Bearer 1:MEUCIQDG+Pwf82nVTQC07Kkmt6YkDbYo/uEsRu8f6f3HCXGQ2AIgJFlltu51Y6lNJ1sEHMviqOnoc75k1A6Qn3xoLBxM1qw=' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36' \
-H 'Content-Type: application/json;charset=UTF-8' \
-H 'Origin: http://202.38.93.111:10100' \
-H 'Referer: http://202.38.93.111:10100/' \
-H 'Accept-Language: en-US,en;q=0.9' \
--data-binary '{"type":"debit"}' \
--compressed \
--insecure
在 Linux(需要安装 curl
)命令行上运行这条命令,再刷新网页,就会发现也能办出储蓄卡。实际上,对于这道题的后端服务器来说,这个请求重要的部分只有:
- 发送
POST
请求给http://202.38.93.111:10100/api/create
。 - 包含 HTTP 请求头:
Authorization: Bearer 1:MEUCIQDG+Pwf82nVTQC07Kkmt6YkDbYo/uEsRu8f6f3HCXGQ2AIgJFlltu51Y6lNJ1sEHMviqOnoc75k1A6Qn3xoLBxM1qw=
,注意其中包含着 token,这说明了你在操作哪个账号。 - 包含 HTTP 请求头:
Content-Type: application/json;charset=UTF-8
,这说明了这个请求的正文是 JSON 格式的。 - 包含请求正文:
{"type":"debit"}
。
除了使用 curl
命令以外,还可以用各种编程语言发送这个请求,方法上网搜索很容易找到。
再经过一些尝试,不难找到办信用卡的请求、吃饭的请求、以及转账的请求(有的请求的正文中有可以改变的参数,表示卡号等信息)。这样就有了完成这道题所需要的所有请求。可以使用复制粘贴加一点修改的方法在一份文档中整理好需要按顺序执行的所有请求,然后全部执行,当然还是推荐学习一下怎么写变量和循环语句。
这道题的创意来自于曾经在一个群里看到有人说自己在现实中这么做,买了很多很多份理财产品来实现利率翻倍,每份几十元,每次需要用钱时取出来几份即可。平时我也常想余额宝之类的产品实际上应该怎么处理类似问题,没想到确实可以实践。
实际出题时,为了让各种数字看起来比较合理,并且解出题目所需要的操作次数不太多,还是花了不少力气凑数字的(例如:利率、饭钱、初始资金、获胜条件等)。实测用附件中提供的(并不是最节约操作次数的)解题脚本通过网络做这道题,一般都能在 10 分钟内做完,符合“预期解法不应当需要很长时间或昂贵机器”的目标。遗憾是这导致手工进行所有操作所需要的时间也较短,在 7 天的比赛中完全可以完成,一些选手因此误以为手工操作也是这道题的一种预期解法,不仅没有借助这道题学会编写脚本发请求,反而浪费了大量时间和精力。今后出题应当避免类似情况,如果预期解需要自动化操作,就不应当让手工操作有完成的可能性。