Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

disasm: Detect init code #23

Merged
merged 6 commits into from
Apr 9, 2023
Merged

disasm: Detect init code #23

merged 6 commits into from
Apr 9, 2023

Conversation

shazow
Copy link
Owner

@shazow shazow commented Apr 6, 2023

Test case code: 60806040526000805534801561001457600080fd5b50610178806100246000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806329e46078146100675780634e148ce11461009a57806368e5c066146100cd578063a87d942c146100e4575b600080fd5b34801561007357600080fd5b50610098600480360381019080803563ffffffff16906020019092919050505061010f565b005b3480156100a657600080fd5b506100cb600480360381019080803563ffffffff169060200190929190505050610122565b005b3480156100d957600080fd5b506100e2610135565b005b3480156100f057600080fd5b506100f9610143565b6040518082815260200191505060405180910390f35b8060020263ffffffff1660008190555050565b8060010263ffffffff1660008190555050565b600160005401600081905550565b600080549050905600a165627a7a72305820946aaa67044121e8026c839756e8dc0bcd00731a5c7239ace3524046974de6720029

Now returns selectors: "0x29e46078" "0x4e148ce1" "0x68e5c066" "0xa87d942c"

We also tack on the init code as a separate sub-program onto the program object.

TODO:

  • Add test
  • Double check against other contracts

CC: #19 @Taryax

@Taryax
Copy link

Taryax commented Apr 7, 2023

Thanks for the quick fix.
However, it seems disasm returns correct selectors but the JUMPDEST offsets are shifted. Making abiFromBytecode return an empty set by disqualifying every selector candidate.
Do you have the same behavior ?

Not sure about the best solution, either modify the BytecodeIter code to be correct or to apply an "init_offset" to every p.dests offset

@shazow
Copy link
Owner Author

shazow commented Apr 7, 2023

Great point, I forgot to adjust that. I originally wrote the code assuming input comes from getCode rather than piped in compiled contracts, so I need to update some assumptions.

src.ts/disasm.ts Outdated
@@ -298,6 +299,33 @@ export function disasm(bytecode: string): Program {
}
}

// Did we just hit the end of init code?
// CODECOPY PUSH1 0x00 RETURN
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further investigation, this won't generalize.

For example, using immutable adds a bunch of code between CODECOPY and RETURN. We'll need to add another mini state machine.

(via https://twitter.com/RenanRSouza35/status/1644308118375915523)

Copy link
Owner Author

@shazow shazow Apr 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sample contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

contract Foo {
    uint public immutable a;

    constructor(uint _a) {
        a = _a;
    }
}
[00]	PUSH1	a0
[02]	PUSH1	40
[04]	MSTORE	
[05]	CALLVALUE	
[06]	DUP1	
[07]	ISZERO	
[08]	PUSH2	0010
[0b]	JUMPI	
[0c]	PUSH1	00
[0e]	DUP1	
[0f]	REVERT	
[10]	JUMPDEST	
[11]	POP	
[12]	PUSH1	40
[14]	MLOAD	
[15]	PUSH2	0191
[18]	CODESIZE	
[19]	SUB	
[1a]	DUP1	
[1b]	PUSH2	0191
[1e]	DUP4	
[1f]	CODECOPY	
[20]	DUP2	
[21]	DUP2	
[22]	ADD	
[23]	PUSH1	40
[25]	MSTORE	
[26]	DUP2	
[27]	ADD	
[28]	SWAP1	
[29]	PUSH2	0032
[2c]	SWAP2	
[2d]	SWAP1	
[2e]	PUSH2	007b
[31]	JUMP	
[32]	JUMPDEST	
[33]	DUP1	
[34]	PUSH1	80
[36]	DUP2	
[37]	DUP2	
[38]	MSTORE	
[39]	POP	
[3a]	POP	
[3b]	POP	
[3c]	PUSH2	00a8
[3f]	JUMP	
[40]	JUMPDEST	
[41]	PUSH1	00
[43]	DUP1	
[44]	REVERT	
[45]	JUMPDEST	
[46]	PUSH1	00
[48]	DUP2	
[49]	SWAP1	
[4a]	POP	
[4b]	SWAP2	
[4c]	SWAP1	
[4d]	POP	
[4e]	JUMP	
[4f]	JUMPDEST	
[50]	PUSH2	0058
[53]	DUP2	
[54]	PUSH2	0045
[57]	JUMP	
[58]	JUMPDEST	
[59]	DUP2	
[5a]	EQ	
[5b]	PUSH2	0063
[5e]	JUMPI	
[5f]	PUSH1	00
[61]	DUP1	
[62]	REVERT	
[63]	JUMPDEST	
[64]	POP	
[65]	JUMP	
[66]	JUMPDEST	
[67]	PUSH1	00
[69]	DUP2	
[6a]	MLOAD	
[6b]	SWAP1	
[6c]	POP	
[6d]	PUSH2	0075
[70]	DUP2	
[71]	PUSH2	004f
[74]	JUMP	
[75]	JUMPDEST	
[76]	SWAP3	
[77]	SWAP2	
[78]	POP	
[79]	POP	
[7a]	JUMP	
[7b]	JUMPDEST	
[7c]	PUSH1	00
[7e]	PUSH1	20
[80]	DUP3	
[81]	DUP5	
[82]	SUB	
[83]	SLT	
[84]	ISZERO	
[85]	PUSH2	0091
[88]	JUMPI	
[89]	PUSH2	0090
[8c]	PUSH2	0040
[8f]	JUMP	
[90]	JUMPDEST	
[91]	JUMPDEST	
[92]	PUSH1	00
[94]	PUSH2	009f
[97]	DUP5	
[98]	DUP3	
[99]	DUP6	
[9a]	ADD	
[9b]	PUSH2	0066
[9e]	JUMP	
[9f]	JUMPDEST	
[a0]	SWAP2	
[a1]	POP	
[a2]	POP	
[a3]	SWAP3	
[a4]	SWAP2	
[a5]	POP	
[a6]	POP	
[a7]	JUMP	
[a8]	JUMPDEST	
[a9]	PUSH1	80
[ab]	MLOAD	
[ac]	PUSH1	d1
[ae]	PUSH2	00c0
[b1]	PUSH1	00
[b3]	CODECOPY	
[b4]	PUSH1	00
[b6]	PUSH1	49
[b8]	ADD	
[b9]	MSTORE	
[ba]	PUSH1	d1
[bc]	PUSH1	00
[be]	RETURN	
[bf]	INVALID	
[c0]	PUSH1	80
[c2]	PUSH1	40
[c4]	MSTORE	
[c5]	CALLVALUE
...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could parse the CODECOPY arguments to find where the runtime code begins

@shazow
Copy link
Owner Author

shazow commented Apr 8, 2023

Immutable case still fails, but otherwise we have the CALLVALUE ... ISZERO thing passing and init code offset passing.

@shazow
Copy link
Owner Author

shazow commented Apr 9, 2023

Okay, this isn't perfect, since I'm not sure we could reliably detect non-static init code without dynamic analysis, but the examples we have so far are passing now!

@shazow shazow merged commit 9ac3233 into main Apr 9, 2023
@shazow shazow deleted the init-code branch April 9, 2023 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants