Fork me on GitHub
pikachu's Blog

第一届钓鱼城杯 strictmathematician WP

前言

  • 第一届钓鱼城杯,2020线上智博会
  • strictmathmaticianWP
  • 题目很简单,考察的其实是storage存储,主要涉及动态数组和map类型数据
  • 题目没给的那部分代码其实只是对map(address)对应的数组长度的限制,如果题目分析的比较透彻,这部分是不会造成影响的,因为比赛只有一天,后来直接给了完整的源码

Source

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
pragma solidity ^0.4.23;

contract StrictMathematician {
address owner;
string private constant welcome = "Oh, fantansitic baby! I am a strict mathematician";
uint randomNumber = 0;
uint createtime = now;

constructor() public payable{
owner = msg.sender;
}

struct Target {
function() internal callback;
uint32 value;
address origin;
address sender;
bytes12 hash;
uint time;
}
Target[] Targets;

struct FailLog {
uint idx;
address origin;
uint time;
bytes12 guessnum;
address sender;
}
mapping(address => FailLog[]) FailLogs;
event SendFlag(address addr);

function start(bytes12 hash) public payable {
Target target;
target.origin = tx.origin;
target.sender = msg.sender;
target.hash = hash;
require(msg.value == 1 ether);
target.value += 1;
Targets.push(target);
}

function guess(uint idx, bytes12 num) public {
if (bytes12(keccak256(abi.encodePacked(num))) != Targets[idx].hash) {
FailLog faillog;
faillog.idx = idx;
faillog.time = now;
faillog.origin = tx.origin;
faillog.sender = msg.sender;
faillog.guessnum = num;
FailLogs[msg.sender].push(faillog);
} else {
Target target = Targets[idx];
target.value += 1;
}
}

function check(uint idx, uint tmp) public {
uint maxlen = check_len(address(msg.sender)) + tmp * 3 / 4 ;
require(uint(read_slot(uint(cal_mapaddr(uint(msg.sender),4)))) <= maxlen);
require(tmp != 0);
Target target = Targets[idx+tmp];
require(uint32(target.value+1)==0);
target.callback();
}

function payforflag() public payable {
require(address(this).balance == 0);
emit SendFlag(msg.sender);
selfdestruct(msg.sender);
}

function read_slot(uint k) internal view returns (bytes32 res) {
assembly { res := sload(k) }
}

function cal_mapaddr(uint k, uint p) internal pure returns(bytes32 res) {
res = keccak256(abi.encodePacked(k, p));
}

function cal_arrayaddr(uint p) internal pure returns(bytes32 res) {
res = keccak256(abi.encodePacked(p));
}

function check_len(address addr) internal pure returns(uint maxlen){

uint res = uint(cal_arrayaddr(uint(cal_mapaddr(uint(addr),4))));
uint sum = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
uint begin = uint(keccak256(abi.encodePacked(uint(3))));
uint distance;
uint remainder;

if (res>begin) {
distance = res - begin;
} else{
distance = sum - begin + res + 1;
}

remainder = distance % 3;
if (remainder==0) {
maxlen = 1;
} else if (remainder==1) {
maxlen = 3;
} else {
maxlen = 2;
}
}
}

Analyse

  • 调用 start(0x000000000000000000000000)
  • 调用 guess(0, 0xffffffff0000000000000153) 一次
  • 计算 target = kecaak256(keccak256(addr|4))+3
  • 计算 base = keccak256(3)
  • distance = (2**256-base+target) % (2**256), idx = distance // 3

    • distance % 3 = 0, 则 idx = idx
    • distance % 3 = 1, 则 idx += 4 ,调用 guess(0, 0xffffffff0000000000000153) 两次
    • distance % 3 = 2, 则 idx += 2 ,掉用 guess(0, 0xffffffff0000000000000153) 一次
  • 调用 guess(0, 0xffffffff0000000000000153) 三次

  • 调用 check(idx, 4) ,其中 tmp4 的倍数,在调用 check 之前需调用 guess(0, 0xffffffff0000000000000153) 的次数为 tmp*3/4
---------------- The End ----------------
谢谢大爷~

Author:pikachu
Link:https://hitcxy.com/2020/strictmathematician/
Contact:hitcxy.cn@gmail.com
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处,谢谢!