Cover photo

SUI交互之suins域名注册参数逆向分析及脚本验证(干货)

SUI交互之suins域名注册参数逆向分析及脚本验证

开发语言:go

Discord交流: Link

Twitter:@ananpt0

项目主页:

https://www.suins.io/

手动测试分析:

首先我们手动注册个域名,然后去Sui Explorer查看记录:

需要2次transaction
需要2次transaction

https://explorer.sui.io/

第一次
第一次

第一次调用了合约0xf6d55ad60e110c1faf2b54c1824df19481536fd5的 base_controller模块的make_commitment_and_commit函数,该函数我们看到有两个参数,第一个是个地址字符串,第二个是个整型数组。

我们继续分析这两个参数含义,点击base_controller去到合约详情页查看合约Bytecode,找到make_commitment_and_commit函数定义:

entry public make_commitment_and_commit(Arg0: &mut BaseController, Arg1: vector, Arg2: &mut TxContext)

第一个参数为BaseController的Object ID

post image

第二个参数是个vector,暂时还不知道是怎么生成的,这个时候我们来调试下前端,看这个参数是怎么生成并传给钱包的。

在合约Bytecode中我们看下虚拟机字节码,看到make_commitment有调用keccak256函数,我们去前端js里面搜索下看看,通过搜索我们发现在main.xxx.js中我们找到一个keccak_256调用:

post image

我们在这一行下断点,然后点击“Register name”按钮试试,成功断下,然后我们可以发现,keccack_256参数为一个Uint8Array,由申请域名字符串+钱包地址(去除0x后16进制数组)+15位字符串,此时15位字符串我们还不知道是怎么生成的,我们可以通过调用栈回溯来找到生成的地方:

post image

点击A下面那个匿名函数:

post image

我们可以发现这个15位字符串是用过y.We.internet.password()生成的15位随机字符串。

所以第一个Transaction两个参数为:BaseController的Object ID,keccak256后的结果int数组。

接下来我们看看第二个Transaction:

第一步还是查看Sui Explorer记录,找到Bytecode中register函数

entry public register(Arg0: &mut BaseController, Arg1: &mut BaseRegistrar, Arg2: &mut Registry, Arg3: &Configuration, Arg4: vector<u8>, Arg5: address, Arg6: u64, Arg7: vector<u8>, Arg8: Coin<SUI>, Arg9: &mut TxContext)

可以看出前4个参数分别为BaseController,BaseRegistrar(sui、move后缀的地址不一样),Registry,Configuration地址,第5个参数为注册的域名去除后缀,第6个参数为注册钱包地址,第7个参数为注册期限(一年的为365),第8个参数为15位随机字符串(第一个Transaction生成的15位随机字符串),最后一个为SUI Coin地址。

到此基本上我们已经完成了参数定义的分析,最后我们用代码来测试看看:

代码实现:

package main

import (
    "encoding/hex"
    "errors"
    "fmt"
    "github.com/coming-chat/go-sui/account"
    "github.com/coming-chat/go-sui/client"
    "github.com/coming-chat/go-sui/types"
    "golang.org/x/crypto/sha3"
    "golang.org/x/net/context"
    "math/rand"
    "net/url"
    "time"
)

const DevnetRpcUrl = "https://fullnode.devnet.sui.io"

var bytes []byte = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_")
var (//地址通过https://explorer.sui.io查找替换
    PackageID         = "0xb43d7bd4394766de04ff82b8214aa8814de69a04"
    BaseController    = "0x50ac890157d0bfda1be5d2e2a0739e75a64281c7"
    SUIBaseRegistrar  = "0x4e2ce97c434f449785bbdce745d418c08d9f1a7f"//sui后缀
    MoveBaseRegistrar = "0xdcea9fe45615172f4d2c3c74a4ed76c565014fa"//move后缀
    Registry          = "0x79af4acd5d1d70fbb29b724b5060c67063cae83c"
    Configuration     = "0x1ea0f7ae66fb52e4b51dc89fe9b02e3767a9d15f"
)

func init() {
    rand.Seed(time.Now().UnixNano())
}

func RandStr(n int) string {
    result := make([]byte, n)
    for i := 0; i < n; i++ {
        result[i] = bytes[rand.Int31()%63]
    }
    return string(result)
}

func Hash(data ...[]byte) []int {
    hash := sha3.NewLegacyKeccak256()
    for _, d := range data {
        hash.Write(d)
    }
    hashBytes := hash.Sum(nil)
    intSlice := make([]int, len(hashBytes))
    for i, b := range hashBytes {
        intSlice[i] = int(b)
    }
    return intSlice
}

func MakeCommitment(domain, address, solt string) []int {
    tmp := make([]byte, 0)
    tmp = append(tmp, []byte(domain)...)
    addr, _ := hex.DecodeString(address)
    tmp = append(tmp, addr...)
    tmp = append(tmp, []byte(solt)...)
    return Hash(tmp)
}

func MakeCommitmentAndCommit(client *client.Client, ctx context.Context, signer types.Address, commitment []int, gas *types.ObjectId, gasBudget uint64) (*types.TransactionBytes, error) {
    packageId, _ := types.NewAddressFromHex(PackageID)
    args := []any{
        BaseController, commitment,
    }
    return client.MoveCall(ctx, signer, *packageId, "base_controller", "make_commitment_and_commit", args, gas, gasBudget)
}

func MakeCommit(mnemonic, domain string) (*types.Base64Data, string, error) {

    acc, _ := account.NewAccountWithMnemonic(mnemonic)
    signer, _ := types.NewAddressFromHex(acc.Address)
    addr := acc.Address[2:]//去除0x
    randStr := RandStr(15)
    hash := MakeCommitment(domain, addr, randStr)
    suiClient, err := client.Dial(DevnetRpcUrl)
    if err != nil {
        return nil, "", err
    }
    gasBudget := uint64(100000)
    txnBytes, err := MakeCommitmentAndCommit(suiClient, context.Background(), *signer, hash, nil, gasBudget)
    if err != nil {
        return nil, "", err
    }
    signedTxn := txnBytes.SignWith(acc.PrivateKey)
    txnResponse, err := suiClient.ExecuteTransaction(context.Background(), *signedTxn, types.TxnRequestTypeWaitForLocalExecution)
    if err != nil {
        return nil, "", err
    }
    return txnResponse.EffectsCert.Effects.Effects.TransactionDigest, randStr, nil
}

func Register(client *client.Client, ctx context.Context, signer types.Address, register, domain, address string, day int, randStr string, coinID string, gas *types.ObjectId, gasBudget uint64) (*types.TransactionBytes, error) {
    packageId, _ := types.NewAddressFromHex(PackageID)
    args := []any{
        BaseController, register, Registry, Configuration, domain, address, day, randStr, coinID,
    }
    return client.MoveCall(ctx, signer, *packageId, "base_controller", "register", args, gas, gasBudget)
}

func DoRegister(mnemonic, domain, suffix, randStr string) (*types.Base64Data, error) {

    acc, _ := account.NewAccountWithMnemonic(mnemonic)
    signer, _ := types.NewAddressFromHex(acc.Address)

    suiClient, err := client.Dial(DevnetRpcUrl)
    if err != nil {
        return nil, err
    }
    gasBudget := uint64(1000000)
    reg := ""
    if suffix == "sui" {
        reg = SUIBaseRegistrar
    } else if suffix == "move" {
        reg = MoveBaseRegistrar
    } else {
        return nil, errors.New("err suffix")
    }
    txnBytes, err := Register(suiClient, context.Background(), *signer, reg, domain, acc.Address, 365, randStr, "COIN_OBJECT_ID", nil, gasBudget)//COIN_OBJECT_ID替换为你钱包中SUI Coin对象ID
    if err != nil {
        return nil, err
    }
    signedTxn := txnBytes.SignWith(acc.PrivateKey)
    txnResponse, err := suiClient.ExecuteTransaction(context.Background(), *signedTxn, types.TxnRequestTypeWaitForLocalExecution)
    if err != nil {
        return nil, err
    }
    return txnResponse.EffectsCert.Effects.Effects.TransactionDigest, nil
}

func main() {
    domain := ""//你要注册的域名名称
    suffix := "sui"//sui或者move后缀
    fmt.Printf("[+][suins]注册域名:%s.%s\n", domain, suffix)
    mnemonic := ""//钱包助记词
    if tid, randStr, err := MakeCommit(mnemonic, domain+"."+suffix); err != nil {
        fmt.Println("[+]", err)
    } else {
        fmt.Printf("[+]make_commitment_and_commit done! Check https://explorer.sui.io/transactions/%s\n", url.QueryEscape(tid.String()))
        if rid, err := DoRegister(mnemonic, domain, suffix, randStr); err != nil {
            fmt.Println("[+]", err)
        } else {
            fmt.Printf("[+]register done! Check https://explorer.sui.io/transactions/%s\n", url.QueryEscape(rid.String()))
        }
    }
}
成功注册
成功注册