Most smart contracts have no verified source code, but people still trust them to protect their cryptocurrency. What’s more, several large custodial smart contracts have had security incidents. The security of contracts that exist on the blockchain should be independently ascertainable.
Ethereum VM (EVM) Bytecode
Ethereum contracts are compiled to EVM – the Ethereum Virtual Machine. As blocks are mined, EVM is executed and its resulting state is encoded into the blockchain forever. Everyone has access to this compiled EVM code for every smart contract on the blockchain — but reviewing EVM directly isn’t easy.
EVM is a RISC Harvard-architecture stack machine, which is fairly distinct in the world of computer architectures. EVM has around 200 instructions which push and pop values from a stack, occasionally performing specific actions on them (e.g. ADD takes two arguments of the stack, adds them together, and pushes the result back to the stack). If you’re familiar with reverse polish notation (RPN) calculators, then stack machines will appear similar. Stack machines are easy to implement but difficult to reverse-engineer. As a reverse-engineer, I have no registers, local variables, or arguments that I can label and track when looking at a stack machine.
Rattle is an EVM binary static analysis framework designed to work on deployed smart contracts. Rattle takes EVM byte strings, uses a flow-sensitive analysis to recover the original control flow graph, lifts the control flow graph into an SSA/infinite register form, and optimizes the SSA – removing DUPs, SWAPs, PUSHs, and POPs. Converting the stack machine to SSA form removes 60%+ of EVM instructions and presents a much friendlier interface to those who wish to read the smart contracts they’re interacting with.
As an example, we will analyze the infamous King of Ether contract.
First in Ethersplay, our Binary Ninja plug-in for analyzing Ethereum Smart Contracts:
In Ethersplay, we can see there are 43 instructions and 5 basic blocks. The majority of the instructions are pure stack manipulation instructions (e.g. PUSH, DUP, SWAP, POP). Interspersed in the blocks are the interesting instructions (e.g. CALLVALUE, SLOAD, etc.).
Now, analyze the contract with Rattle and observe the output for the same function. We run Rattle with optimizations, so constants are folded and unneeded blocks are removed.
$ python3 rattle-cli.py --input inputs/kingofether/KingOfTheEtherThrone.bin -O
The Rattle CLI interface generates graphviz files for each function that it can identify and extract.
As you can see, Rattle optimized the numberOfMonarchs() function to only 12 instructions. Rattle eliminated 72% of the instructions, assigned registers you can track visually, and removed an entire basic block. What’s more, Rattle recovered the used storage location and the ABI of the function.
Rattle will help organizations and individuals study the contracts they’re interacting with and establish an informed degree of trust to the contracts’ security. If your contracts’ source code isn’t available or can’t be verified, then you should run Rattle.
Get Rattle on our GitHub and try it out for yourself.