-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPuzzleBoxSolution_V2.sol
99 lines (76 loc) · 8.52 KB
/
PuzzleBoxSolution_V2.sol
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
pragma solidity ^0.8.19;
import "./PuzzleBox.sol";
import "forge-std/Test.sol";
/// This solution further optimizes PuzzleBoxSolution_V1 using codecopy() to copy the
/// encoded function selectors + Helper contract bytecode from environment code to memory,
/// to avoid 'add(selectoors, specificSelectorOffset)' opcodes during calls to zip, creep, torch, spread, open.
/// Replaing '0' with 'callvalue()' further saves 1 gas as 'PUSH1 0x00' is 3 gas, while CALLVALUE is 2 gas.
/// Reference: https://www.kalos.xyz/blog/dragonfly-ctf-puzzlebox-sol
contract PuzzleBoxSolution_V2 {
function solve(PuzzleBox _puzzle) external payable {
// How close can you get to opening the box?
// function selectors of leak(), zip(), creep(), torch(), spread(), open()
// bytes memory selectoors = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000";
// dynamically stored bytecode
// bytes memory helperCode = type(Helper).creationCode;
// assembly-optimized bytecode with compiler optimizations enabled to 1,000,000 runs
// bytes memory helperCode = hex"60806040527f7159a618deecedd4925facb10000000000000000000000000000000000000000600052600080600460008073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060008060446004600073037eda3adb1198021a9b2e88c22b464fd38db3f35af15060b3806100776000396000f3fe6080604052610151471460008114601b5760018114606457607b565b7f9f678cca0000000000000000000000000000000000000000000000000000000060005260008060046000606573037eda3adb1198021a9b2e88c22b464fd38db3f35af150607b565b73037eda3adb1198021a9b2e88c22b464fd38db3f5ff5b00fea2646970667358221220cd1ba594295bd87e73888e1b54a24556a64e88bd4f377490115ed520d55d50fc64736f6c63430008130033";
assembly {
// copy encoded selectors + Helper contract bytecode from environment code (at 'garage' below) to memory, to avoid 'add(selectoors, specificSelectorOffset)' opcodes for
// calls to zip, creep, torch, spread, open
// codecopy(0x20, 469, 1020)
// codecopy(0x20, 509, 1020)
codecopy(0x20, 477, 1020)
// test call to log calldata at 0x320
// pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x320, 252, callvalue(), callvalue()))
// deploy Helper contract and call its fallback function
let helperAddr := create(callvalue(), 0x320, 252)
pop(call(gas(), helperAddr, callvalue(), callvalue(), callvalue(), callvalue(), callvalue()))
// leak()
pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x24, 4, callvalue(), callvalue()))
// warming up puzzle + 2 // not needed now as it's been done in Helper's fallback
// pop(call(gas(), add(_puzzle, 2), 1, 0, 0, 0, 0))
// zip()
pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x28, 4, callvalue(), callvalue()))
// creep()
pop(call(98000, 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x2c, 4, callvalue(), callvalue()))
// torch()
pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x30, 293, callvalue(), callvalue()))
// spread()
pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x155, 260, callvalue(), callvalue()))
// open()
pop(call(gas(), 0x69209d8a7d258515eC9a4D25F7Be1dB85cB1B826, callvalue(), 0x259, 196, callvalue(), callvalue()))
}
// encoded function selectors of leak(), zip(), creep(), torch(), spread(), open() + Helper contract bytecode, so that
// it can be copied from environment code to memory at 0x20 using codecopy()
bytes memory garage = hex"7159a6188fd66f250091905511551052925facb1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000092b071e47000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000416e59dacfdb5d457304115bbfb9089531d873b70000000000000000000000000000000000000000000000000000000000000003000000000000000000000000c817dd2a5daa8f790677e399170c92aabd044b570000000000000000000000000000000000000000000000000000000000000096000000000000000000000000000000000000000000000000000000000000004b58657dcfc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000041c8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d89da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a621b00000000000000000000000000000000000000000000000000000000000000000000608060405260006b7159a618deecedd4925facb160a01b81527369209d8a7d258515ec9a4d25f7be1db85cb1b826818260048485855af15081826044600485855af150505060aa806100526000396000f3fe60806040524761015114801560175760018114605d57005b7f9f678cca000000000000000000000000000000000000000000000000000000006000526000806004600060657369209d8a7d258515ec9a4d25f7be1db85cb1b8265af1005b7369209d8a7d258515ec9a4d25f7be1db85cb1b828fffea26469706673582212207d8240af97bb21dbf56dba721b468de6e8a15dc3dc21e7a0175cb7595a37898f64736f6c63430008130033";
}
}
/* Commented-out Helper contract, as its deployment bytecode is hardcoded above, to save gas */
// contract Helper {
// constructor() payable {
// // bytes memory data = hex"7159a618deecedd4925facb1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
// // console.logBytes4(bytes4(keccak256("torch(bytes)")));
// // console.logBytes(abi.encodeWithSignature("lock(bytes4,bool)", bytes4(keccak256("torch(bytes)")), false));
// assembly {
// mstore(0x00, hex"7159a618deecedd4925facb10000000000000000000000000000000000000000")
// // operate
// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x00, 4, 0, 0))
// // lock()
// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, 0, 0x04, 68, 0, 0))
// }
// }
// fallback() external payable {
// assembly {
// switch eq(selfbalance(), 337)
// case 0 {
// mstore(0, hex"9f678cca")
// pop(call(gas(), 0x037eDa3aDB1198021A9b2e88C22B464fD38db3f3, selfbalance(), 0x00, 4, 0, 0))
// }
// case 1 {
// // warming puzzle + 2
// selfdestruct(0x037eDa3aDB1198021A9b2e88C22B464fD38db3f5)
// }
// }
// }
// }