智能合约平安系列文章反汇编·上篇

前言

通过上一篇反编译文章的学习,我们对智能合于opcode的反编译有了基础的学习,对于初学者来说,要想熟练运用还得多加练习。本篇我们来一块学习智能合约反汇编,同样使用的是Online Solidity Decompiler在线网站,智能合约反汇编对于初学者来说,较难明白,但对于智能合约代码来说,只要能读懂智能合约反汇编,就可以异常清晰的领会到合约的代码逻辑,对审计合约和CTF智能合约都有异常大的辅助

反汇编内容

由于solidity智能合约的opcode经由反汇编后,指令较多,我们本篇剖析简明要义,以一段简朴合约代码来剖析其反汇编后的指令内容

合约源码如下:

pragma solidity ^0.4.24;

contract Tee {

    uint256 private c;

    function a() public returns (uint256) { self(2); }

    function b() public { c++; }

    function self(uint n) internal returns (uint256) {

        if (n <= 1) { return 1; }

        return n * self(n - 1);
    }
}

合约部署后天生的opcode:

0x6080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630dbe671f14604e5780634df7e3d0146076575b600080fd5b348015605957600080fd5b506060608a565b6040518082815260200191505060405180910390f35b348015608157600080fd5b5060886098565b005b60006094600260ab565b5090565b6000808154809291906001019190505550565b600060018211151560be576001905060cd565b60c86001830360ab565b820290505b9190505600a165627a7a7230582003f585ad588850fbfba4e8d96684e2c3fa427daf013d4a0f8e78188d4d475ee80029

通过在线网站Online Solidity Decompiler反汇编后效果(runtime bytecode)如下:

电银付(dianyinzhifu.com):智能合约平安系列文章反汇编·上篇 安全技术 区块链安全 第1张

反汇编剖析

我们从第一部门指令label_0000最先

0000    60  PUSH1 0x80
    0002    60  PUSH1 0x40
    0004    52  MSTORE
    0005    60  PUSH1 0x04
    0007    36  CALLDATASIZE
    0008    10  LT
    0009    60  PUSH1 0x49
    000B    57  *JUMPI

push指令是将字节压入栈顶,push1-push32依次代表将1字节-32字节推压入栈顶,这里PUSH1 0x80和PUSH1 0x40示意将0x80和0x40压入栈顶,故现在栈的结构如下:

1: 0x40
0: 0x80

MSTORE指令示意从栈中依次出栈两个值arg0和arg1,并把arg1存放在内存的arg0处。现在来说栈中已无数据,这里将0x80存放在内存0x40处。

PUSH1 0x04将0x04压入栈中,CALLDATASIZE指令示意获取msg.data挪用数据,现在栈的结构如下:

1: calldata
0: 0x04

LT指令示意将两个栈顶的值取出,若是先出栈的值小于后出栈的值则把1入栈,反之把0入栈。这里若是calldata挪用数据小于0x04字节,就将1入栈;若是calldata挪用数据大于即是0x04字节,就将0入栈。现在栈的结构为:0: 0 或0: 1。

继续剖析,PUSH1 0x49指令将0x49压入栈顶,现在栈的结构为:

1:0x49
0: 0 或者 1

下面一条指令JUMPI指令示意从栈中依次出栈两个值arg0和arg1,若是arg1的值为真则跳转到arg0处,否则不跳转。若是arg1值为1,则指令会跳转到0x49处;若是arg1值为0,则会顺序执行下一条指令。详细执行历程如下:

电银付(dianyinzhifu.com):智能合约平安系列文章反汇编·上篇 安全技术 区块链安全 第2张

这里我们先来剖析顺序执行的内容label_000C,指令如下

000C    60  PUSH1 0x00
    000E    35  CALLDATALOAD
    000F    7C  PUSH29 0x0100000000000000000000000000000000000000000000000000000000
    002D    90  SWAP1
    002E    04  DIV
    002F    63  PUSH4 0xffffffff
    0034    16  AND
    0035    80  DUP1
    0036    63  PUSH4 0x0dbe671f
    003B    14  EQ
    003C    60  PUSH1 0x4e
    003E    57  *JUMPI

现在经由上一步运算栈中结构为空,PUSH1 0x00指令将0压入栈中。CALLDATALOAD指令接受一个参数,该参数可以作为发往智能合约的calldata数据的索引,然后从该索引处再读取32字节数,由于前一个指令传入的索引值为0,以是这一步指令会弹出栈中的0,将calldata32字节压入栈中。PUSH29指令将29个字节压入栈中。现在栈的结构如下:

,

欧博亚洲_ALLbet6.com

欢迎进入欧博亚洲(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

,
1:0x0100000000000000000000000000000000000000000000000000000000
0:calldata值

SWAP1指令示意将客栈顶部元素与之后的第一个元素举行交流,也就是0x0100000000000000000000000000000000000000000000000000000000和calldata值举行交流。接下来DIV指令示意(栈中第一个元素 // 栈中第二个元素)取a//b的值,这里也就是calldata的32字节除29字节,由于除法的运算关系,这里举行除法运算后的字节为4位,估量人人也可以想到,这就是函数标识符4字节。那么现在栈的结构如下:

0:函数标识符4字节

PUSH4 指令将0xffffffff压入栈中。AND指令示意将取栈中前两个参数举行AND运算,也就是函数标识符前四位0xffffffff举行AND操作,最终获得前四位的函数标识符及后28位为空补0的数值。下一条指令DUP1示意复制当前栈中第一个值到栈顶,现在栈中结构如下:

1:挪用参数中的函数标识符
0:挪用参数中的函数标识符

下一个指令PUSH4指令继续将函数标识符0x0dbe671f压入栈中,这里的标识符为a()函数,函数标识符我们可以在https://www.4byte.directory/在线网站查看。现在栈中结构如下:

2:0x0dbe671f
1:挪用参数中的函数标识符
0:挪用参数中的函数标识符

EQ指令示意取两个栈顶值,若是两值相等就将1入栈(也就是说a()函数标识符与挪用参数中的函数标识符相等),反之将0入栈。下一步PUSH1将0x4e压入栈顶。之后JUMPI指令从栈中依次出栈两个值arg0和arg1,若是arg1的值为真则跳转到arg0处,否则不跳转。现在栈中结构如下:

2:0x4e
1:1 或 0 
0:挪用参数中的函数标识符

从前面三个指令可看出,EQ对函数标识符举行判断后,下一步压入0x4e是为了JUMPI举行判断并跳转。也就是说若是EQ判断a()函数标识符相等(将1入栈),JUMPI执行后就会跳转到0x4e的偏移位置;反之若是EQ判断a()函数标识符不相等(将0入栈),JUMPI执行后就会顺序执行下一条语句。现在栈中结构如下:

0:挪用参数中的函数标识符

详细执行历程如下:

电银付(dianyinzhifu.com):智能合约平安系列文章反汇编·上篇 安全技术 区块链安全 第3张

现在我们对label_0000和label_000C已举行剖析,从上图来看,该流程中除了顺序执行外,label_0000处0x49,label_003F处0x76和label_000C处0x4e都有响应的跳转条件。本篇我们继续剖析顺序执行部门(label_003F和label_0049)指令。首先来看第一部门label_003F:

003F    80  DUP1
    0040    63  PUSH4 0x4df7e3d0
    0045    14  EQ
    0046    60  PUSH1 0x76
    0048    57  *JUMPI

由于现在栈中只有一条数据(0:挪用参数中的函数标识符)

DUP1指令示意复制栈中第一个值到栈顶。PUSH4指令将0x4df7e3d0函数标识符压入栈顶,这里函数标识符代表b()函数,故现在栈中结构如下:

2:0x4df7e3d0
1:挪用参数中的函数标识符
0:挪用参数中的函数标识符

接下来三个指令会举行栈中值举行运算和偏移量跳转设置,EQ指令把栈顶的两个值出栈,若是0x4df7e3d0和挪用参数中的函数标识符相等则把1入栈,否则把0入栈。PUSH1指令将偏移量0x76压入栈中。JUMPI指令从栈中依次出栈两个值:0x76和EQ指令判断的值(1或0),若是EQ指令判断的值为真则跳转到0x76处,否则按顺序执行不跳转。故现在栈中结构如下:

2:0x76
1:1 或 0 
0:挪用参数中的函数标识符

我们假设EQ指令判断的值为0,那么通过JUMPI指令条件判断后,会根据顺序继续执行下一条指令。执行后,栈中依然只有一条指令(0:挪用参数中的函数标识符)。

我们继续举行顺序执行,label_0049:

0049    5B  JUMPDEST
    004A    60  PUSH1 0x00
    004C    80  DUP1
    004D    FD  *REVERT

JUMPDEST指令在该上下文中示意跳转回来,也就是label_0000处0x49的跳转。之后的两条指令PUSH1和DUP1总体意思为将0压入栈顶并复制,没有实际意义。REVERT指令则示意并未有函数署名匹配,从而住手执行,回滚状态。

总结

由于反汇编内容过多,我们分为两篇分享给人人,本篇我们对反汇编的内容举行了详细解说,下篇我们将会继续剖析并串联所有指令,梳理代码逻辑。


电银付声明:该文看法仅代表作者自己,与本平台无关。转载请注明:电银付(dianyinzhifu.com):智能合约平安系列文章反汇编·上篇
发布评论

分享到:

usdt支付接口(www.caibao.it):美军无人机午夜侦探南海,或成抵近侦探新力量
2 条回复
  1. allbetgaming
    allbetgaming
    (2020-12-23 00:00:30) 1#

    小伙伴来啦

  2. 皇冠APP
    皇冠APP
    (2021-01-18 00:00:17) 2#

    Allbetwww.allbetgame.us欢迎进入欧博亚洲(Allbet Game),Allbet是欧博亚洲的官方网站。欧博亚洲开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。目测这网站,这文会火

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。