虎符区块链与Bytecode编写调试
合约如下:
contract Challenge {
uint constant fee = 100000;
uint constant max_code_size = 0x80;
event SendFlag(address);
function solve() public {
uint answer;
bool success;
bytes memory result;
assembly {
answer := extcodesize(caller())
}
require(answer < max_code_size);
(success, result) = msg.sender.staticcall{gas:fee}("");
answer = uint(bytes32(result));
require(success && answer == 1);
(success, result) = msg.sender.staticcall{gas:fee}("");
answer = uint(bytes32(result));
require(success && answer == 2);
emit SendFlag(msg.sender);
}
}
题目目标很简单,就是要求2次staticcall的情况下需要通过改变返回值 来通过校验。
其中有几处限制:
- 合约字节码大小 小于0x80
- 合约需要自己来call题目合约
- 合约能够不改变自身状态来返回2种不同的值
我们考虑发现他设置了阈值gas 也就是我们可能可以通过判断gas 来实现2种不同的返回值。那么如何判断呢?
首先能够考虑到的是这个gas 在不够使用,就是不足100000的情况下,他会投入全部gas进行call
那么这里就是一个关键点。我们call solve的同时不给它足够的gas即可。然后通过一个gas阈值来判断2种状态。
那么难点来到bytecode编写。
其实0x80这个限制较低了。正常手写应该可以优化到0x60以下。
当然可能这对于没有写过的人比较不友好。这里贴一个我的shellcode
CALLER
PUSH20 0x1bdDCdA2d1914Fb966237f32df6db10BB3fC3983
EQ
PUSH1 0x1e
JUMPI
JUMPDEST
PUSH1 0x4b
JUMP
JUMPDEST
PUSH1 0x00
PUSH1 0x50
PUSH4 0x890d6908
PUSH1 0x34
MSTORE
PUSH1 0x04
PUSH1 0x50
PUSH1 0x00
PUSH20 0xc8aDfc432C3a29B54e89CaaFFC51A936C73f64b1
PUSH3 0x00fbfb
CALL
JUMPDEST
GAS
PUSH3 0x00f000
LT
PUSH1 0x59
JUMPI
JUMPDEST
PUSH1 0x64
JUMP
JUMPDEST
PUSH1 0x01
PUSH1 0x80
MSTORE
PUSH1 0x20
PUSH1 0x80
Return
JUMPDEST
PUSH1 0x02
PUSH1 0x80
MSTORE
PUSH1 0x20
PUSH1 0x80
RETURN
可以自己手写完之后 将bytecode反编译看看跳转逻辑是否正确即可。或者 Debug跟一遍就可以了。
并不会非常困难。
最后利用部署字节码模板部署一下即可。
然后利用自己的账户去call一下合约即可。