Token sale
1. 题目
1.1 This token contract allows you to buy and sell tokens at an even exchange rate of 1 token per ether.
The contract starts off with a balance of 1 ether. See if you can take some of that away.
1.2 源码:
1 | pragma solidity ^0.4.21; |
2. 分析
2.1 分析代码可知,如果按部就班来操作(即 用相同的钱来买相同的代币是行不通的—-因为
buy
函数中的require(msg.value == numTokens * PRICE_PER_TOKEN); balanceOf[msg.sender] += numTokens;
和sell
函数中的require(balanceOf[msg.sender] >= numTokens); balanceOf[msg.sender] -= numTokens;
限制了你存多少取多少,不能存少取多)2.2 所以按部就班行不通,但是我们只能往存少取的方向取考虑,这就涉及了溢出的问题
2.3 以 uint8为例子,我们可以知道
在uint8 的加法中
1
2
3>254 + 1 = 255
>254 + 2 = 0
>254 + 3 = 1我在remix中尝试过,直接显示的写
uint8 = 255 + 1
是会被编译器检测出来的,但是隐式的写则不会即1
2>uint8 a = 255;
>uint8 b = a + 1;这样输出的结果是
0
;在uint8 的乘法中
1
2>51 * 5 = 255
>51 * 6 = 5051 * 6 = 50
可以根据加法拆分为51 * 6 = 51 * (5 + 1) = 51 * 5 + 51 = 255 + 1 + 50 = 50
我的理解是超过 255重新计数,因为uint8 的取值范围是 [0,255] 256 位,所以超过255 的部分又从 0 开始。
同理 uint256 类型也是如此,uint256的取值范围是[0,115792089237316195423570985008687907853269984665640564039457584007913129639935]
2.4 我们本着存少取多的原则,让
require(msg.value == numTokens * PRICE_PER_TOKEN);
中的numTokens * PRICE_PER_TOKEN
发生溢出,我们就可以实现花费少的主币获取更多的代币2.5 在合约中主币是以 wei为单位的 也就是说
uint256 constant PRICE_PER_TOKEN = 1 ether = 10^18 wei
我们就需要计算出溢出的门槛是多少 ,计算如下:
1
2
3
4
5
6
7//115792089237316195423570985008687907853269984665640564039457584007913129639935
uint256 result = 2**256 - 1;
//115792089237316195423570985008687907853269984665640564039457
uint256 temp = result / 10**18;
// 此时给temp 的值加 1 应该是触发溢出的最低门槛
uint256 money = (temp + 1) * 10**18; //money=415992086870360064在remix上测试得:

2.6 然后再调用buy函数的时候,以
temp+1
的值作为参数传入,money的值作为msg.value
2.7 以 1 为参数调用 sell函数

解题成功
3. 解题
解题过程如上述分析
一段时间时候,二刷
攻击合约
1 | contract Hack { |