티스토리 뷰

문제

https://app.web3oj.com/app/problem/21

컨트랙트 코드도 없고 ABI도 없는 상황에서 특정 함수를 호출하고 싶은 경우에 바이트 코드를 활용하는 방법.


바이트코드

  • foundry cast cli를 통해 런타임 바이트코드 불러오기
$ cast code 0x9843A771650a28de6d9ba52C38ca37F8870989c2 --rpc-url mumbai
0x608060405234801561001057600080fd5b50600436106100415760003560e01c806338cc483114610046578063a146bf7a14610064578063da17c60514610082575b600080fd5b61004e6100b2565b60405161005b91906101aa565b60405180910390f35b61006c6100db565b60405161007991906101aa565b60405180910390f35b61009c600480360381019061009791906101f6565b6100ff565b6040516100a991906101aa565b60405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061019482610169565b9050919050565b6101a481610189565b82525050565b60006020820190506101bf600083018461019b565b92915050565b600080fd5b6101d381610189565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b60006020828403121561020c5761020b6101c5565b5b600061021a848285016101e1565b9150509291505056fea26469706673582212202f68af6f647b37f8b50512357c14687c5f31ef468a185fa58ba044cd33d003d964736f6c63430008110033
  • foundry cast cli를 통해 바이트코드를 opcode로 변환
$ cast code 0x9843A771650a28de6d9ba52C38ca37F8870989c2 --rpc-url mumbai -d
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: CALLVALUE
00000006: DUP1
00000007: ISZERO
00000008: PUSH2 0x10
0000000b: JUMPI
0000000c: PUSH1 0x0
0000000e: DUP1
0000000f: REVERT
00000010: JUMPDEST
00000011: POP
00000012: PUSH1 0x4
00000014: CALLDATASIZE
00000015: LT
00000016: PUSH2 0x41
00000019: JUMPI
0000001a: PUSH1 0x0
0000001c: CALLDATALOAD
0000001d: PUSH1 0xe0
0000001f: SHR
00000020: DUP1
00000021: PUSH4 0x38cc4831
00000026: EQ
00000027: PUSH2 0x46
0000002a: JUMPI
0000002b: DUP1
0000002c: PUSH4 0xa146bf7a
00000031: EQ
00000032: PUSH2 0x64
00000035: JUMPI
00000036: DUP1
00000037: PUSH4 0xda17c605
0000003c: EQ
0000003d: PUSH2 0x82
00000040: JUMPI
00000041: JUMPDEST
00000042: PUSH1 0x0
00000044: DUP1
00000045: REVERT
00000046: JUMPDEST
00000047: PUSH2 0x4e
0000004a: PUSH2 0xb2
0000004d: JUMP
0000004e: JUMPDEST
0000004f: PUSH1 0x40
00000051: MLOAD
00000052: PUSH2 0x5b
00000055: SWAP2
00000056: SWAP1
00000057: PUSH2 0x1aa
0000005a: JUMP
0000005b: JUMPDEST
0000005c: PUSH1 0x40
0000005e: MLOAD
0000005f: DUP1
00000060: SWAP2
00000061: SUB
00000062: SWAP1
00000063: RETURN
00000064: JUMPDEST
00000065: PUSH2 0x6c
00000068: PUSH2 0xdb
0000006b: JUMP
0000006c: JUMPDEST
0000006d: PUSH1 0x40
0000006f: MLOAD
00000070: PUSH2 0x79
00000073: SWAP2
00000074: SWAP1
00000075: PUSH2 0x1aa
00000078: JUMP
00000079: JUMPDEST
0000007a: PUSH1 0x40
0000007c: MLOAD
0000007d: DUP1
0000007e: SWAP2
0000007f: SUB
00000080: SWAP1
00000081: RETURN
00000082: JUMPDEST
00000083: PUSH2 0x9c
00000086: PUSH1 0x4
00000088: DUP1
00000089: CALLDATASIZE
0000008a: SUB
0000008b: DUP2
0000008c: ADD
0000008d: SWAP1
0000008e: PUSH2 0x97
00000091: SWAP2
00000092: SWAP1
00000093: PUSH2 0x1f6
00000096: JUMP
00000097: JUMPDEST
00000098: PUSH2 0xff
0000009b: JUMP
0000009c: JUMPDEST
0000009d: PUSH1 0x40
0000009f: MLOAD
000000a0: PUSH2 0xa9
000000a3: SWAP2
000000a4: SWAP1
000000a5: PUSH2 0x1aa
000000a8: JUMP
000000a9: JUMPDEST
000000aa: PUSH1 0x40
000000ac: MLOAD
000000ad: DUP1
000000ae: SWAP2
000000af: SUB
000000b0: SWAP1
000000b1: RETURN
000000b2: JUMPDEST
000000b3: PUSH1 0x0
000000b5: DUP1
000000b6: PUSH1 0x0
000000b8: SWAP1
000000b9: SLOAD
000000ba: SWAP1
000000bb: PUSH2 0x100
000000be: EXP
000000bf: SWAP1
000000c0: DIV
000000c1: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
000000d6: AND
000000d7: SWAP1
000000d8: POP
000000d9: SWAP1
000000da: JUMP
000000db: JUMPDEST
000000dc: PUSH1 0x0
000000de: DUP1
000000df: SLOAD
000000e0: SWAP1
000000e1: PUSH2 0x100
000000e4: EXP
000000e5: SWAP1
000000e6: DIV
000000e7: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
000000fc: AND
000000fd: DUP2
000000fe: JUMP
000000ff: JUMPDEST
00000100: PUSH1 0x0
00000102: DUP2
00000103: PUSH1 0x0
00000105: DUP1
00000106: PUSH2 0x100
00000109: EXP
0000010a: DUP2
0000010b: SLOAD
0000010c: DUP2
0000010d: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000122: MUL
00000123: NOT
00000124: AND
00000125: SWAP1
00000126: DUP4
00000127: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
0000013c: AND
0000013d: MUL
0000013e: OR
0000013f: SWAP1
00000140: SSTORE
00000141: POP
00000142: PUSH1 0x0
00000144: DUP1
00000145: SLOAD
00000146: SWAP1
00000147: PUSH2 0x100
0000014a: EXP
0000014b: SWAP1
0000014c: DIV
0000014d: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000162: AND
00000163: SWAP1
00000164: POP
00000165: SWAP2
00000166: SWAP1
00000167: POP
00000168: JUMP
00000169: JUMPDEST
0000016a: PUSH1 0x0
0000016c: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000181: DUP3
00000182: AND
00000183: SWAP1
00000184: POP
00000185: SWAP2
00000186: SWAP1
00000187: POP
00000188: JUMP
00000189: JUMPDEST
0000018a: PUSH1 0x0
0000018c: PUSH2 0x194
0000018f: DUP3
00000190: PUSH2 0x169
00000193: JUMP
00000194: JUMPDEST
00000195: SWAP1
00000196: POP
00000197: SWAP2
00000198: SWAP1
00000199: POP
0000019a: JUMP
0000019b: JUMPDEST
0000019c: PUSH2 0x1a4
0000019f: DUP2
000001a0: PUSH2 0x189
000001a3: JUMP
000001a4: JUMPDEST
000001a5: DUP3
000001a6: MSTORE
000001a7: POP
000001a8: POP
000001a9: JUMP
000001aa: JUMPDEST
000001ab: PUSH1 0x0
000001ad: PUSH1 0x20
000001af: DUP3
000001b0: ADD
000001b1: SWAP1
000001b2: POP
000001b3: PUSH2 0x1bf
000001b6: PUSH1 0x0
000001b8: DUP4
000001b9: ADD
000001ba: DUP5
000001bb: PUSH2 0x19b
000001be: JUMP
000001bf: JUMPDEST
000001c0: SWAP3
000001c1: SWAP2
000001c2: POP
000001c3: POP
000001c4: JUMP
000001c5: JUMPDEST
000001c6: PUSH1 0x0
000001c8: DUP1
000001c9: REVERT
000001ca: JUMPDEST
000001cb: PUSH2 0x1d3
000001ce: DUP2
000001cf: PUSH2 0x189
000001d2: JUMP
000001d3: JUMPDEST
000001d4: DUP2
000001d5: EQ
000001d6: PUSH2 0x1de
000001d9: JUMPI
000001da: PUSH1 0x0
000001dc: DUP1
000001dd: REVERT
000001de: JUMPDEST
000001df: POP
000001e0: JUMP
000001e1: JUMPDEST
000001e2: PUSH1 0x0
000001e4: DUP2
000001e5: CALLDATALOAD
000001e6: SWAP1
000001e7: POP
000001e8: PUSH2 0x1f0
000001eb: DUP2
000001ec: PUSH2 0x1ca
000001ef: JUMP
000001f0: JUMPDEST
000001f1: SWAP3
000001f2: SWAP2
000001f3: POP
000001f4: POP
000001f5: JUMP
000001f6: JUMPDEST
000001f7: PUSH1 0x0
000001f9: PUSH1 0x20
000001fb: DUP3
000001fc: DUP5
000001fd: SUB
000001fe: SLT
000001ff: ISZERO
00000200: PUSH2 0x20c
00000203: JUMPI
00000204: PUSH2 0x20b
00000207: PUSH2 0x1c5
0000020a: JUMP
0000020b: JUMPDEST
0000020c: JUMPDEST
0000020d: PUSH1 0x0
0000020f: PUSH2 0x21a
00000212: DUP5
00000213: DUP3
00000214: DUP6
00000215: ADD
00000216: PUSH2 0x1e1
00000219: JUMP
0000021a: JUMPDEST
0000021b: SWAP2
0000021c: POP
0000021d: POP
0000021e: SWAP3
0000021f: SWAP2
00000220: POP
00000221: POP
00000222: JUMP
00000223: INVALID
00000224: LOG2
00000225: PUSH5 0x6970667358
0000022b: INVALID
0000022c: SLT
0000022d: SHA3
0000022e: INVALID
0000022f: PUSH9 0xaf6f647b37f8b50512
00000239: CALLDATALOAD
0000023a: PUSH29 0x14687c5f31ef468a185fa58ba044cd33d003d964736f6c634300081100
00000258: CALLER

바이트코드 해석

 

EVM Codes

An Ethereum Virtual Machine Opcodes Interactive Reference

www.evm.codes

  • 'a: b' 형식에서 a는 프로그램 카운터(배포된 바이트코드의 바이트 오프셋), b는 opcode를 나타낸다.
00000000: PUSH1 0x80 # 1바이트 0x80을 스택에 추가
00000002: PUSH1 0x40 # 1바이트 0x40을 스택에 추가
00000004: MSTORE 	 # 메모리의 offset이 0x40인 위치에 0x80(value)을 32바이트로 저장
# memory before MSTORE
0

# memory after MSTORE
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000 # offset 64바이트
0000000000000000000000000000000000000000000000000000000000000080 # value 32바이트
00000005: CALLVALUE # callvalue를 스택에 추가
00000006: DUP1 # 스택의 가장 위에 있는 callvalue를 복사하여 스택에 추가
00000007: ISZERO # 스택 가장 위에 있는 값을 꺼낸 뒤 그 값이 0인 경우 1을, 그렇지 않은 경우 0을 스택에 추가
00000008: PUSH2 0x10 # 2바이트 0x0010을 스택에 추가
0000000b: JUMPI
  • JUMPI는 스택의 위에서 2개의 원소를 가져와 각각 counter와 b로 읽는다.
  • counter(프로그램 카운터)가 가리키는 위치에는 반드시 JUMPDEST 명령어가 있어야 한다.
  • b가 0이 아닌 경우에만 counter가 가리키는 위치로 이동하며, 그렇지 않은 경우는 바이트코드의 다음 오프셋을 읽어 들인다.
  • 예를 들어, 현재 코드에서 callvalue가 0이 아니라면 JUMPI 명령어를 실행하기 위해 가져오는 counter와 b는 각각 10과 0이 된다. 따라서 counter가 가리키는 위치로 이동하지 않고 다음 바이트 오프셋을 읽어 들인다.
# callvalue가 0보다 큰 경우
0000000c: PUSH1 0x0 # 1바이트 0x0을 스택에 추가
0000000e: DUP1 # 스택의 가장 위에 있는 0x0을 복사하여 스택에 추가
0000000f: REVERT # offset과 size가 0x0인 데이터와 남은 가스를 반환
  • callvalue가 0보다 큰 경우에는 컨트랙트 호출이 revert 된다. 이는 컨트랙트에 fallback 함수가 정의되어 있지 않아 이더를 송금받을 수 없음을 의미한다.
# callvalue가 0인 경우
00000010: JUMPDEST # JUMPI 명령어에서 counter가 가리킨 위치
00000011: POP # 스택에서 원소를 하나 제거
00000012: PUSH1 0x4 # 스택에 1바이트 0x4를 추가
00000014: CALLDATASIZE # 스택에 calldata의 크기를 추가
00000015: LT # calldata의 크기가 4보다 작으면 1, 크거나 같으면 0을 스택에 추가
00000016: PUSH2 0x41 # 스택에 2바이트 0x0041을 추가
00000019: JUMPI # counter가 0x0041, b가 0 또는 1
  • b가 0이 되려면 calldata의 크기가 4보다 크거나 같아야 한다. 이는 함수 호출을 위한 선택자를 calldata에 포함하고 있음을 의미한다. 반대로 b가 1이 되려면 calldata의 크기가 4보다 작아야 하며, 이는 함수를 호출하는 것이 아니라는 것을 유추할 수 있다.
# calldata 크기가 4보다 크거나 같은 경우
0000001a: PUSH1 0x0 # 스택에 1바이트 0x0을 추가
0000001c: CALLDATALOAD # 스택 가장 위에 있는 원소를 가져와 32바이트의 offset으로 사용하여 calldata의 offset부터 32바이트를 스택에 추가
0000001d: PUSH1 0xe0 # 스택에 1바이트 0xe0을 추가
0000001f: SHR # 스택 가장 위의 원소를 shift할 크기, 두 번째 원소를 shift할 값으로 가져와 shift 연산을 실행한 결과를 스택에 추가
00000020: DUP1 # 스택 가장 위의 원소를 복사하여 스택에 추가
00000021: PUSH4 0x38cc4831 # 4바이트 0x38cc4831을 스택에 추가
00000026: EQ # 스택 가장 위의 원소 2개를 꺼내 두 개의 원소가 동일하면 1, 아니면 0을 스택에 추가
00000027: PUSH2 0x46 # 2바이트 0x46을 스택에 추가
0000002a: JUMPI # 스택에서 2개의 원소를 꺼내 2번째 원소가 1이라면 프로그램 카운터가 0x46인 위치로 이동
0000002b: DUP1 # 스택 가장 위의 원소를 복사하여 스택에 추가
0000002c: PUSH4 0xa146bf7a # 4바이트 0xa146bf7a을 스택에 추가
00000031: EQ # 스택 가장 위의 원소 2개를 꺼내 두 개의 원소가 동일하면 1, 아니면 0을 스택에 추가
00000032: PUSH2 0x64 # 2바이트 0x64를 스택에 추가
00000035: JUMPI # 스택에서 2개의 원소를 꺼내 2번째 원소가 1이라면 프로그램 카운터가 0x64인 위치로 이동
00000036: DUP1 # 스택 가장 위의 원소를 복사하여 스택에 추가
00000037: PUSH4 0xda17c605 # 4바이트 0xda17c605을 스택에 추가
0000003c: EQ # 스택 가장 위의 원소 2개를 꺼내 두 개의 원소가 동일하면 1, 아니면 0을 스택에 추가
0000003d: PUSH2 0x82 # 2바이트 0x82를 스택에 추가
00000040: JUMPI # 스택에서 2개의 원소를 꺼내 2번째 원소가 1이라면 프로그램 카운터가 0x82인 위치로 이동
  • calldata의 크기가 4보다 크거나 같은 경우에는 우선 함수 선택자와 매칭되는 함수가 있는지 확인이 필요하다.
# calldata의 크기가 4보다 작은 경우 또는 존재하지 않는 함수를 호출한 경우
00000041: JUMPDEST
00000042: PUSH1 0x0
00000044: DUP1
00000045: REVERT
  •  calldata의 크기가 4보다 작거나 컨트랙트에 존재하지 않는 함수를 호출한 경우에는 revert된다.
# 함수 선택자가 0x38cc4831인 경우
00000046: JUMPDEST
00000047: PUSH2 0x4e # 스택에 2바이트 0x4e를 추가
0000004a: PUSH2 0xb2 # 스택에 2바이트 0xb2를 추가
0000004d: JUMP # 스택 가장 위의 원소를 가져와(0xb2) 이 값이 가리키는 위치로 점프
0000004e: JUMPDEST
0000004f: PUSH1 0x40 # 스택에 1바이트 0x40 추가
00000051: MLOAD # 메모리의 offset이 0x40인 위치부터 32바이트를 읽어들임 => 0x80
00000052: PUSH2 0x5b # 스택에 2바이트 0x5b 추가
00000055: SWAP2 # 스택에 3개의 원소 a, b, c 중에서 a와 c의 위치를 변경
00000056: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
00000057: PUSH2 0x1aa # 2바이트 0x1aa를 스택에 추가
0000005a: JUMP # 프로그램 카운터가 0x1aa인 위치로 이동
0000005b: JUMPDEST
0000005c: PUSH1 0x40
0000005e: MLOAD
0000005f: DUP1
00000060: SWAP2
00000061: SUB
00000062: SWAP1
00000063: RETURN # 스택 가장 위의 원소를 offset, 그 다음을 size로 가져와 메모리의 offset부터 size만큼의 데이터를 읽어 반환한다
# 함수 선택자가 0xa146bf7a인 경우
00000064: JUMPDEST
00000065: PUSH2 0x6c
00000068: PUSH2 0xdb
0000006b: JUMP # db로 이동
0000006c: JUMPDEST
0000006d: PUSH1 0x40
0000006f: MLOAD
00000070: PUSH2 0x79
00000073: SWAP2
00000074: SWAP1
00000075: PUSH2 0x1aa
00000078: JUMP
00000079: JUMPDEST
0000007a: PUSH1 0x40
0000007c: MLOAD
0000007d: DUP1
0000007e: SWAP2
0000007f: SUB
00000080: SWAP1
00000081: RETURN
# 함수 선택자가 0xda17c605인 경우
00000082: JUMPDEST
00000083: PUSH2 0x9c
00000086: PUSH1 0x4
00000088: DUP1
00000089: CALLDATASIZE
0000008a: SUB
0000008b: DUP2
0000008c: ADD
0000008d: SWAP1
0000008e: PUSH2 0x97
00000091: SWAP2
00000092: SWAP1
00000093: PUSH2 0x1f6
00000096: JUMP
00000097: JUMPDEST
00000098: PUSH2 0xff
0000009b: JUMP
0000009c: JUMPDEST
0000009d: PUSH1 0x40
0000009f: MLOAD
000000a0: PUSH2 0xa9
000000a3: SWAP2
000000a4: SWAP1
000000a5: PUSH2 0x1aa
000000a8: JUMP
000000a9: JUMPDEST
000000aa: PUSH1 0x40
000000ac: MLOAD
000000ad: DUP1
000000ae: SWAP2
000000af: SUB
000000b0: SWAP1
000000b1: RETURN

# 함수 선택자가 0x38cc4831인 경우
000000b2: JUMPDEST
000000b3: PUSH1 0x0
000000b5: DUP1
000000b6: PUSH1 0x0
000000b8: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
000000b9: SLOAD # 스택 가장 위의 원소를 key로 가져와 스토리지에서 key가 가리키는 값을 스택에 추가
000000ba: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
000000bb: PUSH2 0x100 # 스택에 2바이트 0x100을 추가
000000be: EXP # 스택 가장 위의 원소를 a, 그 다음 원소를 exponet로 가져와 a ** exponent를 구하여 스택에 추가
000000bf: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
000000c0: DIV # 스택 가장 위의 원소를 a, 그 다음 원소를 b로 가져와 a // b (소수점 이하 버림)를 구하여 스택에 추가
000000c1: PUSH20 0xffffffffffffffffffffffffffffffffffffffff # 스택에 20바이트 0xffffffffffffffffffffffffffffffffffffffff를 추가
000000d6: AND # 스택 위의 2개의 원소를 가져와 비트 연산 &를 실행한 결과를 스택에 추가
000000d7: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
000000d8: POP # 스택 가장 위의 원소를 제거
000000d9: SWAP1 # 스택 위의 2개의 원소의 위치를 변경
000000da: JUMP # 연산 결과를 가지고 0x4e로 되돌아 간다
# 함수 선택자가 0xa146bf7a인 경우
000000db: JUMPDEST
000000dc: PUSH1 0x0
000000de: DUP1
000000df: SLOAD
000000e0: SWAP1
000000e1: PUSH2 0x100
000000e4: EXP
000000e5: SWAP1
000000e6: DIV
000000e7: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
000000fc: AND
000000fd: DUP2
000000fe: JUMP # 6c로 이동
000000ff: JUMPDEST
00000100: PUSH1 0x0
00000102: DUP2
00000103: PUSH1 0x0
00000105: DUP1
00000106: PUSH2 0x100
00000109: EXP
0000010a: DUP2
0000010b: SLOAD
0000010c: DUP2
0000010d: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000122: MUL
00000123: NOT
00000124: AND
00000125: SWAP1
00000126: DUP4
00000127: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
0000013c: AND
0000013d: MUL
0000013e: OR
0000013f: SWAP1
00000140: SSTORE
00000141: POP
00000142: PUSH1 0x0
00000144: DUP1
00000145: SLOAD
00000146: SWAP1
00000147: PUSH2 0x100
0000014a: EXP
0000014b: SWAP1
0000014c: DIV
0000014d: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000162: AND
00000163: SWAP1
00000164: POP
00000165: SWAP2
00000166: SWAP1
00000167: POP
00000168: JUMP
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
# 함수 선택자가 0xda17c605인 경우
00000169: JUMPDEST
0000016a: PUSH1 0x0
0000016c: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000181: DUP3
00000182: AND
00000183: SWAP1
00000184: POP
00000185: SWAP2
00000186: SWAP1
00000187: POP
00000188: JUMP # 0x194로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
00000189: JUMPDEST
0000018a: PUSH1 0x0
0000018c: PUSH2 0x194
0000018f: DUP3
00000190: PUSH2 0x169
00000193: JUMP # 0x169로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
# 함수 선택자가 0xda17c605인 경우
00000194: JUMPDEST
00000195: SWAP1
00000196: POP
00000197: SWAP2
00000198: SWAP1
00000199: POP
0000019a: JUMP # 1a4로 이동, 1d3(0xda17c605)으로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
0000019b: JUMPDEST
0000019c: PUSH2 0x1a4
0000019f: DUP2
000001a0: PUSH2 0x189
000001a3: JUMP # 0x189로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
000001a4: JUMPDEST
000001a5: DUP3
000001a6: MSTORE
000001a7: POP
000001a8: POP
000001a9: JUMP # 0x1bf로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
000001aa: JUMPDEST
000001ab: PUSH1 0x0
000001ad: PUSH1 0x20
000001af: DUP3 # 스택 위에서 3번째 원소를 복사하여 스택에 추가
000001b0: ADD # 스택 위의 2개의 원소를 꺼내 더한 값을 스택에 추가
000001b1: SWAP1
000001b2: POP
000001b3: PUSH2 0x1bf
000001b6: PUSH1 0x0
000001b8: DUP4 # 스택 위에서 4번째 원소를 복사하여 스택에 추가
000001b9: ADD
000001ba: DUP5 # 스택 위에서 5번째 원소를 복사하여 스택에 추가
000001bb: PUSH2 0x19b
000001be: JUMP # 0x19b로 이동
# 함수 선택자가 0x38cc4831인 경우
# 함수 선택자가 0xa146bf7a인 경우
000001bf: JUMPDEST
000001c0: SWAP3
000001c1: SWAP2
000001c2: POP
000001c3: POP
000001c4: JUMP # 0x38cc4831 -> 5b로 이동, 0xa146bf7a -> 79로 이동
# 함수 선택자가 0xda17c605인 경우
000001c5: JUMPDEST
000001c6: PUSH1 0x0
000001c8: DUP1
000001c9: REVERT
# 함수 선택자가 0xda17c605인 경우
000001ca: JUMPDEST
000001cb: PUSH2 0x1d3
000001ce: DUP2
000001cf: PUSH2 0x189
000001d2: JUMP # 189로 이동
000001d3: JUMPDEST
000001d4: DUP2
000001d5: EQ
000001d6: PUSH2 0x1de
000001d9: JUMPI
000001da: PUSH1 0x0
000001dc: DUP1
000001dd: REVERT
000001de: JUMPDEST
000001df: POP
000001e0: JUMP
# 함수 선택자가 0xda17c605인 경우
000001e1: JUMPDEST
000001e2: PUSH1 0x0
000001e4: DUP2
000001e5: CALLDATALOAD
000001e6: SWAP1
000001e7: POP
000001e8: PUSH2 0x1f0
000001eb: DUP2
000001ec: PUSH2 0x1ca
000001ef: JUMP # DUP2의 결과가 0이 아니라면 1ca로 이동
000001f0: JUMPDEST
000001f1: SWAP3
000001f2: SWAP2
000001f3: POP
000001f4: POP
000001f5: JUMP
# 함수 선택자가 0xda17c605인 경우
000001f6: JUMPDEST
000001f7: PUSH1 0x0
000001f9: PUSH1 0x20
000001fb: DUP3
000001fc: DUP5
000001fd: SUB
000001fe: SLT # 스택 위의 원소 2개를 부호가 있는 정수 a, b로 가져와 a가 b보다 작은 경우 1, 아닌 경우 0을 스택에 추가
000001ff: ISZERO
00000200: PUSH2 0x20c
00000203: JUMPI # ISZERO의 결과가 1이라면 0x20c로 이동
00000204: PUSH2 0x20b
00000207: PUSH2 0x1c5
0000020a: JUMP # 1c5로 이동
# 함수 선택자가 0xda17c605인 경우
0000020b: JUMPDEST
0000020c: JUMPDEST
0000020d: PUSH1 0x0
0000020f: PUSH2 0x21a
00000212: DUP5
00000213: DUP3
00000214: DUP6
00000215: ADD
00000216: PUSH2 0x1e1
00000219: JUMP # 1e1로 이동
0000021a: JUMPDEST
0000021b: SWAP2
0000021c: POP
0000021d: POP
0000021e: SWAP3
0000021f: SWAP2
00000220: POP
00000221: POP
00000222: JUMP
00000223: INVALID
00000224: LOG2
00000225: PUSH5 0x6970667358
0000022b: INVALID
0000022c: SLT
0000022d: SHA3
0000022e: INVALID
0000022f: PUSH9 0xaf6f647b37f8b50512
00000239: CALLDATALOAD
0000023a: PUSH29 0x14687c5f31ef468a185fa58ba044cd33d003d964736f6c634300081100
00000258: CALLER
  • 바이트코드를 확인해 본 결과, 총 3개의 함수 선택자 0x38cc4831, 0xa146bf7a, 0xda17c605를 확인할 수 있다. 즉, 이 컨트랙트에는 3개의 함수가 정의되어 있다는 것이다.
  • 이 중 0x38cc4831, 0xa146bf7a는 calldata를 사용하지 않는다.
  • 0xda17c605는 calldata를 사용하여 상태를 변경하는 것으로 보인다.
  • 문제에서는 주소를 등록하는 함수의 형식이  'funcName(address)'와 같다고 하였으니 calldata를 사용하는 0xda17c605와 call을 사용해 함수를 호출할 수 있다.


Solidity Decompiler

 

Online Solidity Decompiler

 

ethervm.io

 디컴파일러를 사용해 대강적인 solidity 컨트랙트 구조를 파악할 수 있다. 다만 문제의 컨트랙트가 polygon mumbai 네트워크에 배포되어 있고 해당 네트워크에 대해서는 지원을 하지 않기 때문에 앞서 opcode를 읽어보는 식으로 문제를 해결했다.


Foundry cast 활용하기 (2024.02.14 추가)

  • 런타임 바이트코드를 입력으로 selectors 명령어를 실행하면 함수 선택자와 함수의 파라미터 타입을 손쉽게 확인할 수 있다.
$ cast selectors $(cast code 0x9843A771650a28de6d9ba52C38ca37F8870989c2 --rpc-url mumbai)
0x38cc4831
0xa146bf7a
0xda17c605      address
  • 함수 선택자를 입력으로 4byte 명령어를 실행하면 매칭되는 함수 시그니처를 https://openchain.xyz로부터 찾아 출력한다. 
$ cast 4byte 0x38cc4831
getAddress()
$ cast 4 0xa146bf7a
playerAddress()
$ cast 4 0xda17c605
Error: 
No signature found

 

최근에 올라온 글
최근에 달린 댓글
«   2024/11   »
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
Total
Today
Yesterday
글 보관함