For educational purpose I've created a simple python based evm bytecode disassembler (see github::ethereum-dasm) that attempts to resolve static jumps (JUMP
,JUMPI
instructions) in order to make the disassembly more readable. The disassembler is working fine, however I'm very surprised that some static jumps do not seem to land anywhere near a JUMPDEST
even though thats required by the ethereum vm (as outlined in the ethereum yellowpaper)
A static jump looks as follows:
PUSH <iteral>
JUMP[I]
Here's an excerpt of a contract taken from the ethereum chain (etherchain/contracts/postcats):
Solidity code:
contract postCats {
string strCat;
function set(string x) {
strCat = x;
}
function get() constant returns (string retVal) {
return strCat;
}
}
Disassembly:
#> python evmdasm.py -v critical 0x6060604052610209806100126000396000f3606060405260e060020a60003504634ed3885e81146100265780636d4ce63c146100cc575b005b60206004803580820135601f810184900490930260809081016040526060848152610024946024939192918401918190838280828437509496505050505050508060006000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101a057805160ff19168380011785555b5061019b9291505b808211156101d0578381556001016100b9565b61012d60006060818152815460a06020601f60026000196101006001871615020190941693909304928301819004028101604052608082815292939190828280156101ff5780601f106101d4576101008083540402835291602001916101ff565b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561018d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b505050565b828001600101855582156100b1579182015b828111156100b15782518260005055916020019190600101906101b2565b5090565b820191906000526020600020905b8154815290600101906020018083116101e257829003601f168201915b505050505090509056
Instr.# addrs. mnemonic operand xrefs description
------------------------------------------------------------------------------------------------------------------------------------------------------
[ 0] [0x00000000] PUSH1 0x60 ('`') # Place 1 byte item on stack.
[ 1] [0x00000002] PUSH1 0x40 ('@') # Place 1 byte item on stack.
[ 2] [0x00000004] MSTORE # Save word to memory.
[ 3] [0x00000005] PUSH2 0x0209 # Place 2-byte item on stack.
[ 4] [0x00000008] DUP1 # Duplicate 1st stack item.
[ 5] [0x00000009] PUSH2 0x0012 # Place 2-byte item on stack.
[ 6] [0x0000000c] PUSH1 0x00 # Place 1 byte item on stack.
[ 7] [0x0000000e] CODECOPY # Copy code running in current environment to memory.
[ 8] [0x0000000f] PUSH1 0x00 # Place 1 byte item on stack.
[ 9] [0x00000011] RETURN # Halt execution returning output data.
[ 10] [0x00000012] PUSH1 0x60 ('`') # Place 1 byte item on stack.
[ 11] [0x00000014] PUSH1 0x40 ('@') # Place 1 byte item on stack.
[ 12] [0x00000016] MSTORE # Save word to memory.
[ 13] [0x00000017] PUSH1 0xe0 # Place 1 byte item on stack.
[ 14] [0x00000019] PUSH1 0x02 # Place 1 byte item on stack.
[ 15] [0x0000001b] EXP # Exponential operation.
[ 16] [0x0000001c] PUSH1 0x00 # Place 1 byte item on stack.
[ 17] [0x0000001e] CALLDATALOAD # Get input data of current environment.
[ 18] [0x0000001f] DIV # Integer division operation.
[ 19] [0x00000020] PUSH4 0x4ed3885e # Place 4-byte item on stack.
[ 20] [0x00000025] DUP2 # Duplicate 2nd stack item.
[ 21] [0x00000026] EQ JUMPI@0x2a # Equality comparison <--- INVALID, not a JUMPDEST
[ 22] [0x00000027] PUSH2 0x0026 # Place 2-byte item on stack.
[ 23] [0x0000002a] JUMPI @0x26 # Conditionally alter the program counter.
[ 24] [0x0000002b] DUP1 # Duplicate 1st stack item.
[ 25] [0x0000002c] PUSH4 0x6d4ce63c ('mL\xe6<') # Place 4-byte item on stack.
[ 26] [0x00000031] EQ # Equality comparison
[ 27] [0x00000032] PUSH2 0x00cc # Place 2-byte item on stack.
[ 28] [0x00000035] JUMPI @0xcc # Conditionally alter the program counter.
:loc_0x36
[ 29] [0x00000036] JUMPDEST # Mark a valid destination for jumps. <--- NO JMP REF
[ 30] [0x00000037] STOP # Halts execution.
:loc_0x38
[ 31] [0x00000038] JUMPDEST # Mark a valid destination for jumps. <---- NO JMP REF
[ 32] [0x00000039] PUSH1 0x20 # Place 1 byte item on stack.
...
[ 130] [0x000000b2] LT # Lesser-than comparison
[ 131] [0x000000b3] PUSH2 0x01a0 # Place 2-byte item on stack.
[ 132] [0x000000b6] JUMPI @0x1a0 # Conditionally alter the program counter. <---- VALID
[ 133] [0x000000b7] DUP1 # Duplicate 1st stack item.
[ 134] [0x000000b8] MLOAD # Load word from memory.
[ 135] [0x000000b9] PUSH1 0xff JUMP@0xdd # Place 1 byte item on stack. <---- INVALID, not a JUMPDEST
...
:loc_0xcb
[ 150] [0x000000cb] JUMPDEST # Mark a valid destination for jumps.
[ 151] [0x000000cc] DUP1 JUMPI@0x35 # Duplicate 1st stack item. <---- INVALID, not a JUMPDEST
[ 152] [0x000000cd] DUP3 # Duplicate 3rd stack item.
[ 153] [0x000000ce] GT # Greater-than comparison
[ 154] [0x000000cf] ISZERO # Simple not operator
[ 155] [0x000000d0] PUSH2 0x01d0 # Place 2-byte item on stack.
[ 156] [0x000000d3] JUMPI @0x1d0 # Conditionally alter the program counter.
[ 157] [0x000000d4] DUP4 # Duplicate 4th stack item.
[ 158] [0x000000d5] DUP2 # Duplicate 2nd stack item.
[ 159] [0x000000d6] SSTORE # Save word to storage.
[ 160] [0x000000d7] PUSH1 0x01 # Place 1 byte item on stack.
[ 161] [0x000000d9] ADD # Addition operation.
[ 162] [0x000000da] PUSH2 0x00b9 # Place 2-byte item on stack.
[ 163] [0x000000dd] JUMP @0xb9 # Alter the program counter.
...
[ 295] [0x00000186] DUP1 # Duplicate 1st stack item.
[ 296] [0x00000187] DUP3 # Duplicate 3rd stack item.
[ 297] [0x00000188] SUB # Subtraction operation.
[ 298] [0x00000189] DUP1 # Duplicate 1st stack item.
[ 299] [0x0000018a] MLOAD # Load word from memory.
[ 300] [0x0000018b] PUSH1 0x01 # Place 1 byte item on stack.
[ 301] [0x0000018d] DUP4 JUMPI@0x185 # Duplicate 4th stack item. <-- INVALID not a JUMPDEST
[ 302] [0x0000018e] PUSH1 0x20 # Place 1 byte item on stack.
[ 303] [0x00000190] SUB # Subtraction operation.
[ 304] [0x00000191] PUSH2 0x0100 # Place 2-byte item on stack.
[ 305] [0x00000194] EXP # Exponential operation.
[ 306] [0x00000195] SUB # Subtraction operation.
[ 307] [0x00000196] NOT # Bitwise NOT operation.
[ 308] [0x00000197] AND # Bitwise AND operation.
[ 309] [0x00000198] DUP2 # Duplicate 2nd stack item.
[ 310] [0x00000199] MSTORE # Save word to memory.
[ 311] [0x0000019a] PUSH1 0x20 # Place 1 byte item on stack.
[ 312] [0x0000019c] ADD # Addition operation.
[ 313] [0x0000019d] SWAP2 # Exchange 1st and 3rd stack items.
[ 314] [0x0000019e] POP # Remove item from stack.
:loc_0x19f
[ 315] [0x0000019f] JUMPDEST # Mark a valid destination for jumps.
[ 316] [0x000001a0] POP JUMPI@0xb6 # Remove item from stack. <-- INVALID not a JUMPDEST
[ 317] [0x000001a1] SWAP3 # Exchange 1st and 4th stack items.
[ 318] [0x000001a2] POP # Remove item from stack.
[ 319] [0x000001a3] POP # Remove item from stack.
[ 320] [0x000001a4] POP # Remove item from stack.
[ 321] [0x000001a5] PUSH1 0x40 ('@') # Place 1 byte item on stack.
[ 322] [0x000001a7] MLOAD # Load word from memory.
[ 323] [0x000001a8] DUP1 # Duplicate 1st stack item.
[ 324] [0x000001a9] SWAP2 # Exchange 1st and 3rd stack items.
[ 325] [0x000001aa] SUB # Subtraction operation.
[ 326] [0x000001ab] SWAP1 # Exchange 1st and 2nd stack items.
[ 327] [0x000001ac] RETURN # Halt execution returning output data.
:loc_0x1ad
[ 328] [0x000001ad] JUMPDEST # Mark a valid destination for jumps.
[ 329] [0x000001ae] POP # Remove item from stack.
[ 330] [0x000001af] POP # Remove item from stack.
[ 331] [0x000001b0] POP # Remove item from stack.
[ 332] [0x000001b1] JUMP # Alter the program counter.
:loc_0x1b2
[ 333] [0x000001b2] JUMPDEST JUMP@0x1e1 # Mark a valid destination for jumps. <-- VALID
[ 334] [0x000001b3] DUP3 # Duplicate 3rd stack item.
[ 335] [0x000001b4] DUP1 # Duplicate 1st stack item.
[ 336] [0x000001b5] ADD # Addition operation.
[ 337] [0x000001b6] PUSH1 0x01 # Place 1 byte item on stack.
[ 338] [0x000001b8] ADD # Addition operation.
[ 339] [0x000001b9] DUP6 # Duplicate 6th stack item.
[ 340] [0x000001ba] SSTORE # Save word to storage.
[ 341] [0x000001bb] DUP3 # Duplicate 3rd stack item.
[ 342] [0x000001bc] ISZERO # Simple not operator
[ 343] [0x000001bd] PUSH2 0x00b1 # Place 2-byte item on stack.
[ 344] [0x000001c0] JUMPI @0xb1 # Conditionally alter the program counter.
[ 345] [0x000001c1] SWAP2 # Exchange 1st and 3rd stack items.
[ 346] [0x000001c2] DUP3 # Duplicate 3rd stack item.
[ 347] [0x000001c3] ADD # Addition operation.
:loc_0x1c4
[ 348] [0x000001c4] JUMPDEST # Mark a valid destination for jumps.
[ 349] [0x000001c5] DUP3 # Duplicate 3rd stack item.
[ 350] [0x000001c6] DUP2 # Duplicate 2nd stack item.
[ 351] [0x000001c7] GT # Greater-than comparison
[ 352] [0x000001c8] ISZERO # Simple not operator
[ 353] [0x000001c9] PUSH2 0x00b1 # Place 2-byte item on stack.
[ 354] [0x000001cc] JUMPI @0xb1 # Conditionally alter the program counter.
[ 355] [0x000001cd] DUP3 # Duplicate 3rd stack item.
[ 356] [0x000001ce] MLOAD # Load word from memory.
[ 357] [0x000001cf] DUP3 # Duplicate 3rd stack item.
[ 358] [0x000001d0] PUSH1 0x00 JUMPI@0xd3 # Place 1 byte item on stack. <-- INVALID, not a JUMPDEST
[ 359] [0x000001d2] POP # Remove item from stack.
[ 360] [0x000001d3] SSTORE # Save word to storage. <-- INVALID, not a JUMPDEST
[ 361] [0x000001d4] SWAP2 JUMPI@0x12b # Exchange 1st and 3rd stack items.
[ 362] [0x000001d5] PUSH1 0x20 # Place 1 byte item on stack.
[ 363] [0x000001d7] ADD # Addition operation.
[ 364] [0x000001d8] SWAP2 # Exchange 1st and 3rd stack items.
[ 365] [0x000001d9] SWAP1 # Exchange 1st and 2nd stack items.
[ 366] [0x000001da] PUSH1 0x01 # Place 1 byte item on stack.
[ 367] [0x000001dc] ADD # Addition operation.
[ 368] [0x000001dd] SWAP1 # Exchange 1st and 2nd stack items.
[ 369] [0x000001de] PUSH2 0x01b2 # Place 2-byte item on stack.
[ 370] [0x000001e1] JUMP @0x1b2 # Alter the program counter.
:loc_0x1e2
[ 371] [0x000001e2] JUMPDEST JUMPI@0x207 # Mark a valid destination for jumps. <--- VALID
[ 372] [0x000001e3] POP # Remove item from stack.
[ 373] [0x000001e4] SWAP1 # Exchange 1st and 2nd stack items.
[ 374] [0x000001e5] JUMP # Alter the program counter.
:loc_0x1e6
[ 375] [0x000001e6] JUMPDEST # Mark a valid destination for jumps.
[ 376] [0x000001e7] DUP3 # Duplicate 3rd stack item.
[ 377] [0x000001e8] ADD # Addition operation.
[ 378] [0x000001e9] SWAP2 # Exchange 1st and 3rd stack items.
[ 379] [0x000001ea] SWAP1 # Exchange 1st and 2nd stack items.
[ 380] [0x000001eb] PUSH1 0x00 # Place 1 byte item on stack.
[ 381] [0x000001ed] MSTORE # Save word to memory.
[ 382] [0x000001ee] PUSH1 0x20 # Place 1 byte item on stack.
[ 383] [0x000001f0] PUSH1 0x00 # Place 1 byte item on stack.
[ 384] [0x000001f2] SHA3 # Compute Keccak-256 hash.
[ 385] [0x000001f3] SWAP1 # Exchange 1st and 2nd stack items.
:loc_0x1f4
[ 386] [0x000001f4] JUMPDEST # Mark a valid destination for jumps.
[ 387] [0x000001f5] DUP2 # Duplicate 2nd stack item.
[ 388] [0x000001f6] SLOAD # Load word from storage.
[ 389] [0x000001f7] DUP2 # Duplicate 2nd stack item.
[ 390] [0x000001f8] MSTORE # Save word to memory.
[ 391] [0x000001f9] SWAP1 # Exchange 1st and 2nd stack items.
[ 392] [0x000001fa] PUSH1 0x01 # Place 1 byte item on stack.
[ 393] [0x000001fc] ADD # Addition operation.
[ 394] [0x000001fd] SWAP1 # Exchange 1st and 2nd stack items.
[ 395] [0x000001fe] PUSH1 0x20 # Place 1 byte item on stack.
[ 396] [0x00000200] ADD # Addition operation.
[ 397] [0x00000201] DUP1 # Duplicate 1st stack item.
[ 398] [0x00000202] DUP4 # Duplicate 4th stack item.
[ 399] [0x00000203] GT # Greater-than comparison
[ 400] [0x00000204] PUSH2 0x01e2 # Place 2-byte item on stack.
[ 401] [0x00000207] JUMPI @0x1e2 # Conditionally alter the program counter.
[ 402] [0x00000208] DUP3 # Duplicate 3rd stack item.
[ 403] [0x00000209] SWAP1 # Exchange 1st and 2nd stack items.
[ 404] [0x0000020a] SUB # Subtraction operation.
[ 405] [0x0000020b] PUSH1 0x1f # Place 1 byte item on stack.
[ 406] [0x0000020d] AND # Bitwise AND operation.
[ 407] [0x0000020e] DUP3 # Duplicate 3rd stack item.
[ 408] [0x0000020f] ADD # Addition operation.
[ 409] [0x00000210] SWAP2 # Exchange 1st and 3rd stack items.
:loc_0x211
[ 410] [0x00000211] JUMPDEST # Mark a valid destination for jumps.
[ 411] [0x00000212] POP # Remove item from stack.
[ 412] [0x00000213] POP # Remove item from stack.
[ 413] [0x00000214] POP # Remove item from stack.
[ 414] [0x00000215] POP # Remove item from stack.
[ 415] [0x00000216] POP # Remove item from stack.
[ 416] [0x00000217] SWAP1 # Exchange 1st and 2nd stack items.
[ 417] [0x00000218] POP # Remove item from stack.
[ 418] [0x00000219] SWAP1 # Exchange 1st and 2nd stack items.
[ 419] [0x0000021a] JUMP # Alter the program counter.
assemble(disassemble(evmcode))== True
There are a few valid jumps that land on a JUMPDEST
like 0x000001e1
or 0x00000207
but the great majority either references an invalid target address or does not land on a JUMPDEST
. (Note that I've annotated some valid/invalid jumps in the disassembly listing, check for <--
at the end of the line)
For example the jump:
JUMPI@0x2a
lands on aEQ
- which is invalidJUMP@0xdd
lands on aPUSH
- which is invalidJUMPI@0x35
lands on aDUP
- which is invalidJUMPDEST@0x38
seems to be unreachable (even though it might be ref'd by a dynamic jump)JUMPI@0x123
jumps into an instruction - at first sight this must not be invalid and could well be an obfuscation/optimization artifact but disassembling@0x1ff
does not yieldJUMPDEST
but0x20
which is aSHA3
instruction - therefore invalid[ 216] [0x00000120] PUSH2 0x01ff # Place 2-byte item on stack. [ 217] [0x00000123] JUMPI @0x1ff # Conditionally alter the program counter. ... [ 395] [0x000001fe] PUSH1 0x20 # Place 1 byte item on stack. [ 396] [0x00000200] ADD # Addition operation.
There's obviously something wrong with either my assumption about static jumps, the evm bytecode of the contract (saw the same behavior for multiple different contracts in the blockhain) or the way I match jump destinations.
Below is the code that attempts to resolve jump destinations:
self.instruction_at
is a map address
to instruction
self.xrefs
is a map address
to set(jump_sources, ...)
def _update_xrefs(self):
# find all JUMP, JUMPI's
for loc, instruction in ((l,i) for l,i in self.instruction_at.iteritems() if i.name in ("JUMP","JUMPI")):
if instruction.previous and instruction.previous.name.startswith("PUSH"):
instruction.jumpto = int(instruction.previous.operand, 16)
target_instruction = self.instruction_at.get(instruction.jumpto)
if target_instruction and target_instruction.name=="JUMPDEST":
# valid address, valid target
self.xrefs.setdefault(instruction.jumpto,set([]))
self.xrefs[instruction.jumpto] = instruction
target_instruction.xrefs.add(instruction)
Any clue on what's wrong with my xref code and why the majority of static jumps resolve to invalid destinations is well appreciated.
Thanks,
tin
<--
, scroll to the right).