level4 Telephone

tx.addr是原始调用者
msg.sender是调用这个合约的调用者

可以通过让合约调用合约来实现tx.origin不等于msg.sender

level5 Token

减法和加法有溢出,但是0.8版本有内置的检查

level6 Delegation

fallback函数
函数选择器
底层通过不同的值选择不同的函数执行,从而实现执行不同的合约的函数

委托调用,直接执行目标代码的机器码

level7 Force

如果一个合约没有receive函数,那么就不能向这个合约转账

但是有一个自毁函数可以把本合约销毁然后将自身以太币发送到指定地址

selfdestruct(payable _(_to))

level8 Vault

合约上的所有内容都是公开的,所以可以直接通过查看交易记录得到每一个存放的值

solidity内存排布

由变量声明顺序由上至下申请空间,每个插槽32byte,如果插槽位置不够了,就新申请一个,放在新申请的里面

数组,通过计算keccak§得到初始地址,然后再增加index值

重入攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
pragma solidity 0.8.20;

interface IWrappedEther {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
event Deposit(address indexed from, uint256 amount);
event Withdraw(address indexed to, uint256 amount);

function deposit(address to) external payable;

function withdraw(uint256 amount) external;

function withdrawAll() external;

function transfer(address to, uint256 amount) external;

function transferFrom(address from, address to, uint256 amount) external;

function approve(address spender, uint256 amount) external;

function balanceOf(address account) external view returns (uint256);

function allowance(
address owner,
address spender
) external view returns (uint256);
}




contract WrappedEther is IWrappedEther {
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;

function deposit(address to) external payable {
balanceOf[to] += msg.value;
emit Deposit(msg.sender, msg.value);
}

function withdraw(uint256 amount) external {
require(balanceOf[msg.sender] >= amount, "insufficient balance");
balanceOf[msg.sender] -= amount;
sendEth(payable(msg.sender), amount);
emit Withdraw(msg.sender, amount);
}

function withdrawAll() external {
sendEth(payable(msg.sender), balanceOf[msg.sender]);
balanceOf[msg.sender] = 0;
emit Withdraw(msg.sender, balanceOf[msg.sender]);
}

function transfer(address to, uint256 amount) external {
require(balanceOf[msg.sender] >= amount, "insufficient balance");
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
}

function transferFrom(address from, address to, uint256 amount) external {
require(balanceOf[from] >= amount, "insufficient balance");
require(
allowance[from][msg.sender] >= amount,
"insufficient allowance"
);
balanceOf[from] -= amount;
balanceOf[to] += amount;
allowance[from][msg.sender] -= amount;
emit Transfer(from, to, amount);
}

function approve(address spender, uint256 amount) external {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
}

function sendEth(address payable to, uint256 amount) private {
(bool success, ) = to.call{value: amount}("");
require(success, "failed to send ether");
}
}

在withdrawAll的时候触发,先转账,后withdraw,所以可以在receive再调用这个withdrawAll函数导致多次执行操作。