Используем смарт-контракты и блокчейны EVM-сетей в своих корыстных целях

Man

Professional
Messages
2,963
Reaction score
486
Points
83
Что такое блокчейн и EVM-сети?

Блокчейн — это технология, где данные хранятся в распределенной сети. Все участники имеют одинаковую копию данных, а изменения проверяются сетью. Это делает блокчейн прозрачным, надежным и защищенным.

EVM (Ethereum Virtual Machine) — это среда для выполнения смарт-контрактов. EVM-сети, такие как Ethereum, Binance Smart Chain и Polygon, позволяют разработчикам создавать приложения на основе блокчейна с использованием готовых инструментов.

Почему это удобно?

Децентрализация: Заблокировать систему практически невозможно, так как отсутствует единая точка контроля.
Прозрачность и неизменность: После развертывания смарт-контракта его код становится доступен всем и сохраняется навсегда в блокчейне. Изменить контракт нельзя, если только в нем изначально не предусмотрена возможность для этого.

Я Покажу вам пару примеров где это можно использовать.

Первый пример использования:

Представим, у нас есть онлайн-сервис, который по каким-то причинам часто блокируется и теряет часть своих пользователей (условно блокировка домена/хоста/контактов/т.д). Чтобы не терять своих пользователей и клиентов, мы можем использовать блокчейн для координации и информирования их о текущем рабочем адресе/контакте/линке и т.д.

Достаточно, чтобы у наших пользователей было наше приложение, которое общается со смарт-контрактом в блокчейне. В смарт-контракте администратор(мы) может обновлять информацию, например, указывать новый адрес/контакт/линк и т.д. Пользователи, запуская приложение, всегда получают актуальные данные из блокчейна, минуя центральные сервера, которые могут быть заблокированы или недоступны.

Таким образом:

Пользователи всегда остаются на связи: Блокчейн децентрализован, его нельзя заблокировать или "отключить".
Удобство: Достаточно обновить данные в смарт-контракте, и они тут же станут доступны всем пользователям.
Прозрачность: Любой пользователь может проверить, кто обновил данные и когда это было сделано, исключая манипуляции.
Это пример того, как децентрализация и блокчейн могут быть использованы для решения реальных проблем.

Переходим к практике: Установка MetaMask и Развертывание Первого Смарт-Контракта в Сети Ethereum

> 1: Написание смарт-контракта
Используем простой контракт для хранения и обновления домена.

Перед тем как развернуть наш первый смарт-контракт, важно установить и настроить MetaMask — кошелек, который позволит взаимодействовать с блокчейном. Далее мы разберем весь процесс от начала до конца.

1 >>> Установка и настройка MetaMask

Скачиваем MetaMask:

Переходим на официальный сайт MetaMask (https://metamask.io/).
Устанавливаем расширение для браузера (Chrome, Firefox, Edge, Brave).

1.png


2.png


3.png


Создаем кошелек:

Открываем MetaMask и выбираем "Create a Wallet".

4.png


5.png


Задаем надежный пароль (xD).

6.png


Сохраняем секретную фразу из 12 слов. Это важно для восстановления доступа к кошельку, но в примере не буду показывать как, думаю сами резберетесь.

7.png


Далее просто тыкаем done, next, next...

Добавим новую сеть к примеру Polygon (если хотите использовать эфириум можете остаться на нем, тогда просто пропуститие добавление).
Сделаем добавление максимально по простому, перейдем на https://polygonscan.com/ и внизу сайта в подвале есть кнопка "Add Polygon Network", нажимем ее, у вас откроется metamask соглашаемся и добавляем, если же не получается попробуйте в самом метамаске добавить нажав слева сверху на сети и там выбрать polygon.

8.png


9.png


Пополните баланс сети (в нашем случае polygon):
Сделать вы это можете купить крипту или обменять их в обменнике или в ином месте.

10.png


2 >>> Написание смарт-контракта

Теперь мы создадим контракт, который позволит хранить и обновлять домен.

Перейдите на Remix IDE (https://remix.ethereum.org/).
Создайте файл DomainManager.sol и вставьте код (сохраняем ctrl + s)

11.png


12.png


Code:
pragma solidity ^0.8.0;

contract DomainManager {
    address public admin; // Адрес администратора
    string public currentDomain; // Текущий домен

    // Событие для логирования изменений
    event DomainUpdated(string oldDomain, string newDomain);

    // Конструктор: устанавливаем администратора и начальный домен
    constructor(string memory initialDomain) {
        admin = msg.sender; // Устанавливаем администратора
        currentDomain = initialDomain; // Устанавливаем домен
    }

    // Модификатор: проверка, что вызов делает администратор
    modifier onlyAdmin() {
        require(msg.sender == admin, "You are not the admin");
        _;
    }

    // Обновление домена
    function updateDomain(string memory newDomain) public onlyAdmin {
        string memory oldDomain = currentDomain;
        currentDomain = newDomain;
        emit DomainUpdated(oldDomain, newDomain);
    }

    // Получение текущего домена
    function getDomain() public view returns (string memory) {
        return currentDomain;
    }
}

Объяснение кода:

Code:
pragma solidity ^0.8.0;

pragma solidity ^0.8.0;: Указывает, что код совместим с версиями компилятора Solidity 0.8.0 и выше.

Code:
contract DomainManager {
    address public admin; // Адрес администратора
    string public currentDomain; // Текущий домен

contract DomainManager: Определяет новый контракт с именем DomainManager.
address public admin: Переменная для хранения Ethereum-адреса администратора.
string public currentDomain: Переменная для хранения текущего домена. Домен — это строка (например, "https://xss.is"), которую могут видеть пользователи.

Code:
event DomainUpdated(string oldDomain, string newDomain);

event DomainUpdated: Событие для записи в лог изменений домена.
oldDomain: Старый домен.
newDomain: Новый домен.
События позволяют отслеживать изменения в блокчейне. Это полезно для приложений, которые слушают блокчейн и реагируют на изменения.

Code:
constructor(string memory initialDomain) {
    admin = msg.sender; // Устанавливаем администратора
    currentDomain = initialDomain; // Устанавливаем домен
}

constructor: Функция, которая вызывается один раз при развертывании контракта.
string memory initialDomain: Аргумент конструктора для указания начального домена.
admin = msg.sender: Устанавливает администратора. Администратором становится адрес, который развернул контракт.
currentDomain = initialDomain: Устанавливает начальный домен.

Code:
modifier onlyAdmin() {
    require(msg.sender == admin, "You are not the admin");
    _;
}

modifier onlyAdmin: Проверяет, что функцию вызывает только администратор.
msg.sender: Адрес, который вызвал функцию.
require(msg.sender == admin, "You are not the admin");: Если адрес вызывающего не совпадает с admin, выполнение функции прерывается, и ошибка возвращается с текстом "You are not the admin".
_: Указывает, где будет выполняться код основной функции, к которой применяется модификатор.

Code:
function updateDomain(string memory newDomain) public onlyAdmin {
    string memory oldDomain = currentDomain;
    currentDomain = newDomain;
    emit DomainUpdated(oldDomain, newDomain);
}

updateDomain: Функция для обновления домена.
string memory newDomain: Новый домен, который администратор хочет установить.
public: Функцию можно вызывать извне.
onlyAdmin: Проверяет, что вызов сделан администратором.
Логика функции:
Сохраняет текущий домен в переменную oldDomain.
Обновляет currentDomain на новый.
Вызывает событие DomainUpdated, логируя старый и новый домен.

Code:
function getDomain() public view returns (string memory) {
    return currentDomain;
}

getDomain: Функция для получения текущего домена.
public: Доступна для вызова извне.
view: Указывает, что функция только читает данные и не изменяет состояние блокчейна.
returns (string memory): Возвращает строку с текущим доменом.

Как работает контракт.

Развертывание:

При развертывании конструктора задается начальный домен, и развертывающий адрес становится администратором.
Обновление домена:

Администратор вызывает updateDomain, передавая новый домен. Новый домен записывается, а старый фиксируется в логах через событие.

Получение домена:

Любой пользователь может вызвать getDomain, чтобы получить текущий домен.

3 >>> Компиляция контракта

В Remix открываем вкладку "Solidity Compiler".
Убедитесь, что версия компилятора совпадает с pragma solidity ^0.8.0.
Нажмите "Compile DomainManager.sol".

13.png


4 >>> Развертывание контракта

Открываем вкладку "Deploy & Run Transactions".
В поле Environment выберите "WalletConnet", и потом жмем Connect Wallet и подключаем наш MetaMask.

14.png


Убедимся, что мы подключены к сети (например, Polygon).

15.png


В поле "string initialDomain" указываем начальный домен, например: "https://xss.is".
Нажимаем "Deploy" и подтверждаем транзакцию в MetaMask.

16.png


17.png


После подтверждения контракт будет развернут, а его адрес отобразится в Remix.

18.png


Далее копируем наш адрес контракта и запоминаем, он нам нужен будет в дальнейшем (к примеру наш адрес 0xdCe675B975673A569a51d8aDF585c947063aE544)

19.png


5 >>> Работа с контрактом

В разделе Deployed Contracts найдите ваш контракт.
Нажмите на функцию getDomain, чтобы получить текущий домен.
Обновите домен с помощью функции updateDomain, указав новый адрес, например: "https://xss.as".
Проверьте, что домен изменился, снова вызвав функцию getDomain.

Теперь у нас есть работающий смарт-контракт для хранения и обновления домена (На самом деле в нем не только можно хранить домен но и вообще любую строку)

Приступим к написанию приложения для администратора на Next.js

Мы создадим простое приложение, которое позволит администратору:

Подключаться к своему кошельку через MetaMask.
Проверять текущий домен из смарт-контракта.
Обновлять домен, если пользователь является администратором.

2 > Установка Node.js и создание приложения администратора на Next.js

Для разработки приложения администратора с использованием Next.js нам нужно сначала установить Node.js, настроить окружение и затем приступить к созданию интерфейса для управления доменом через блокчейн.

1 >>> Установка Node.js
Проверка Node.js на нашем компьютере:

Открываем терминал или командную строку.
Вводим команду:

Code:
node -v

Если Node.js установлен, мы увидим его текущую версию (например, v18.17.1).

20.png


Если Node.js не установлен:

Переходим на официальный сайт Node.js(https://nodejs.org/).
Скачайте и установите:
LTS: Стабильная версия, по этому рекомендую её.
После установки после проверки опять же проверяем командой node -v.

21.png


2 >>> Создание проекта Next.js

Теперь создадим интерфейс для администратора, который будет взаимодействовать со смарт-контрактом.

Выполняем команду для создания нового проекта (При установке выбираем то что по дефолту советует NextJS):

Code:
npx create-next-app@latest admin-panel
cd admin-panel

22.png


Устанавливаем библиотеку ethers для работы с блокчейном:

Code:
npm install ethers

23.png


2 >>> Интерфейс приложения

Стираем весь код в src/app/page.ts и пишем наш.

Code:
"use client";

import { useState, useEffect } from "react";
import { ethers } from "ethers";

// Адрес и ABI смарт-контракта
const contractAddress = "0xYourContractAddress"; // Замените на наш адрес контракта
const contractABI = [
  {
    inputs: [],
    name: "getDomain",
    outputs: [{ internalType: "string", name: "", type: "string" }],
    stateMutability: "view",
    type: "function",
  },
  {
    inputs: [{ internalType: "string", name: "newDomain", type: "string" }],
    name: "updateDomain",
    outputs: [],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [],
    name: "admin",
    outputs: [{ internalType: "address", name: "", type: "address" }],
    stateMutability: "view",
    type: "function",
  },
];

export default function AdminPanel() {
  const [currentDomain, setCurrentDomain] = useState("Loading...");
  const [newDomain, setNewDomain] = useState("");
  const [isAdmin, setIsAdmin] = useState(false);
  const [currentAccount, setCurrentAccount] = useState(null);

  useEffect(() => {
    fetchDomain();
  }, []);

async function fetchDomain() {
  try {
    const provider = new ethers.JsonRpcProvider("https://polygon-rpc.com");
    const contract = new ethers.Contract(contractAddress, contractABI, provider);
    const domain = await contract.getDomain();
    setCurrentDomain(domain);
  } catch (error) {
    console.error("Error fetching domain:", error.message);
  }
}

  async function checkWalletConnection() {
    try {
      if (!window.ethereum) throw new Error("MetaMask is not installed");
      const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
      setCurrentAccount(accounts[0]);
      checkAdmin(accounts[0]);
    } catch (error) {
      console.error("Error connecting to MetaMask:", error.message);
    }
  }

async function checkAdmin(account) {
  try {
    const provider = new ethers.JsonRpcProvider("https://polygon-rpc.com");
    const contract = new ethers.Contract(contractAddress, contractABI, provider);
    const adminAddress = await contract.admin();

    setIsAdmin(account.toLowerCase() === adminAddress.toLowerCase());
  } catch (error) {
    console.error("Error checking admin status:", error.message);
  }
}


async function updateDomain() {
  try {
    if (!isAdmin) throw new Error("Only admin can update the domain");

    const provider = new ethers.BrowserProvider(window.ethereum);
    const signer = await provider.getSigner();
    const contract = new ethers.Contract(contractAddress, contractABI, signer);

    const tx = await contract.updateDomain(newDomain);
    console.log("Transaction sent:", tx.hash);

    await tx.wait();
    console.log("Domain updated!");

    fetchDomain();
  } catch (error) {
    console.error("Error updating domain:", error.message);
  }
}


  return (
    <div style={{ padding: "20px", fontFamily: "Arial, sans-serif" }}>
      <h1>Admin Panel</h1>
      <div style={{ marginBottom: "20px" }}>
        {currentAccount ? (
          <p>Connected as: {currentAccount}</p>
        ) : (
          <button onClick={checkWalletConnection}>Connect Wallet</button>
        )}
      </div>
      <h2>Current Domain</h2>
      <p>{currentDomain}</p>
      {isAdmin ? (
        <div style={{ marginTop: "20px" }}>
          <h3>Update Domain</h3>
          <input
            type="text"
            value={newDomain}
            onChange={(e) => setNewDomain(e.target.value)}
            placeholder="Enter new domain"
            style={{ padding: "8px", width: "300px" }}
          />
          <button
            onClick={updateDomain}
            style={{
              padding: "8px 16px",
              marginLeft: "10px",
              cursor: "pointer",
              backgroundColor: "#0070f3",
              color: "#fff",
              border: "none",
              borderRadius: "4px",
            }}
          >
            Update
          </button>
        </div>
      ) : (
        <p style={{ color: "red" }}>You are not the admin</p>
      )}
    </div>
  );
}

Не забываем указать наш адрес контракта в этой строке:
const contractAddress = "0xYourContractAddress"; // Заменяем на наш адрес контракта
на это:
const contractAddress = "0xdCe675B975673A569a51d8aDF585c947063aE544"; // Наш созданый адрес который мы должны были запомнить до этого

Так же есть строка с RPC url, я оставлю к Polygon так как пример основан на нем, но так же вы можете поменять на свой (на самом деле эта переменная нигде не юзается я уже запуталься просто)

Code:
//RPC URL
const rpcUrl = "https://polygon-rpc.com";

(Тут у меня VS Code сломался по этому код в блокноте =) )

24.png


Добавьте базовые стили в src/app/globals.css:

Code:
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  background-color: #f5f5f5;
  color: #333;
}

button {
  cursor: pointer;
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  background-color: #0070f3;
  color: #fff;
}

input {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px;
}

25.png


Разбор кода:

Code:
"use client";

import { useState, useEffect } from "react";
import { ethers } from "ethers";

"use client": Указывает, что компонент работает на стороне клиента в Next.js.
useState и useEffect: Хуки React для управления состоянием и выполнения побочных эффектов.
ethers: Библиотека для взаимодействия с блокчейном.

Code:
const contractAddress = "0xdCe675B975673A569a51d8aDF585c947063aE544"; // Адрес контракта
const contractABI = [
  // Описание методов и функций контракта
];
const rpcUrl = "https://polygon-rpc.com"; // RPC для подключения к сети. (не юзается забыл де)

contractAddress: Адрес развернутого смарт-контракта.
contractABI: ABI (Application Binary Interface) описывает функции и методы контракта.
rpcUrl: URL RPC-сервера для подключения к сети.

Code:
const [currentDomain, setCurrentDomain] = useState("Loading...");
const [newDomain, setNewDomain] = useState("");
const [isAdmin, setIsAdmin] = useState(false);
const [currentAccount, setCurrentAccount] = useState(null);

currentDomain: Хранит текущий домен, полученный из контракта.
newDomain: Хранит новый домен, который администратор хочет установить.
isAdmin: Указывает, является ли пользователь администратором.
currentAccount: Текущий подключенный адрес Ethereum.

Code:
useEffect(() => {
  fetchDomain();
}, []);

Запускает функцию fetchDomain один раз при загрузке компонента.
fetchDomain: Получает текущий домен из смарт-контракта.

Code:
async function fetchDomain() {
  try {
    const provider = new ethers.JsonRpcProvider("https://polygon-rpc.com");
    const contract = new ethers.Contract(contractAddress, contractABI, provider);
    const domain = await contract.getDomain();
    setCurrentDomain(domain);
  } catch (error) {
    console.error("Error fetching domain:", error.message);
  }
}

Создает провайдер через JsonRpcProvider для подключения к сети.
Вызывает метод getDomain контракта для получения текущего домена.
Обновляет состояние currentDomain.

Code:
async function checkWalletConnection() {
    try {
      if (!window.ethereum) throw new Error("MetaMask is not installed");
      const accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
      setCurrentAccount(accounts[0]);
      checkAdmin(accounts[0]);
    } catch (error) {
      console.error("Error connecting to MetaMask:", error.message);
    }
  }

Проверяет наличие MetaMask.
Подключается к кошельку через eth_requestAccounts.
Сохраняет текущий аккаунт в состоянии currentAccount.
Проверяет, является ли пользователь администратором, вызывая checkAdmin.

Code:
async function checkAdmin(account) {
  try {
    const provider = new ethers.JsonRpcProvider("https://polygon-rpc.com");
    const contract = new ethers.Contract(contractAddress, contractABI, provider);
    const adminAddress = await contract.admin();

    setIsAdmin(account.toLowerCase() === adminAddress.toLowerCase());
  } catch (error) {
    console.error("Error checking admin status:", error.message);
  }
}

Проверяет, совпадает ли текущий адрес с адресом администратора контракта.
Использует метод admin контракта.

Code:
async function updateDomain() {
  try {
    if (!isAdmin) throw new Error("Only admin can update the domain");

    const provider = new ethers.BrowserProvider(window.ethereum);
    const signer = await provider.getSigner();
    const contract = new ethers.Contract(contractAddress, contractABI, signer);

    const tx = await contract.updateDomain(newDomain);
    console.log("Transaction sent:", tx.hash);

    await tx.wait();
    console.log("Domain updated!");

    fetchDomain();
  } catch (error) {
    console.error("Error updating domain:", error.message);
  }
}

Проверяет права администратора.
Создает провайдер через MetaMask (BrowserProvider).
Подписывает и отправляет транзакцию для обновления домена.
Обновляет состояние после успешной транзакции.

3 >>> Запуск приложения

Запускаем сервер в DEV режиме, открываем консоль в проекте и пишем:

Code:
npm run dev

26.png


Открываем http://localhost:3000 в браузере.
Подключаем MetaMask и проверяем фукнционал:
Отображение текущего домена.
Обновление домена, если вы администратор.
Теперь у нас есть работающий интерфейс для управления доменом через блокчейн!

27.png


Попробуем поменять домен на https://xss.as

Как видим мы ввели домен, и нажали кнопку и нам осталось только подтвердить транзакцию

28.png


После подтверждение мы видим как домен поменялся.

29.png


>>> Создание простенького скрипта для клиента чтоб получать всегда актуальный линк.

Открываем консоль или терминал, cоздаем папку для проекта и переходим в неё:

Code:
mkdir client-app
cd client-app

30.png


Инициализируем проект:

Code:
npm init -y

31.png


Устанавливаем ethers

Code:
npm install ethers

32.png



Создаем файл index.js и пишем туда код.

Code:
const { ethers } = require("ethers");

// Адрес контракта и его ABI
const contractAddress = "0xdCe675B975673A569a51d8aDF585c947063aE544";
const contractABI = [
  {
    inputs: [],
    name: "getDomain",
    outputs: [{ internalType: "string", name: "", type: "string" }],
    stateMutability: "view",
    type: "function",
  },
];

// URL RPC-сервера
const rpcUrl = "https://polygon-rpc.com";

async function fetchDomain() {
  try {
    // Создаем провайдер для подключения к сети Polygon
    const provider = new ethers.JsonRpcProvider(rpcUrl);

    // Подключаем контракт
    const contract = new ethers.Contract(contractAddress, contractABI, provider);

    // Вызываем метод getDomain
    const domain = await contract.getDomain();

    // Выводим домен в консоль
    console.log("Current Domain:", domain);
  } catch (error) {
    console.error("Error fetching domain:", error.message);
  }
}

// Запускаем функцию
fetchDomain();

33.png


Как видим прописав node index.js наш клиент получает актуальный домен.

34.png


Второй пример
Так... ладно.. домены это все понятно, но я лично предпочитаю использовать блокчейн для получения C2 сервера, и сейчас покажу вам пример на С++ без лишних библиотек, только WinAPI который позволяет получать строку (Домен в нашем случаее) из блокчейна.

Приступим сначала я создам NodeJs скрипт для получения hex`a функций.

Так же создаем папку к примеру blockchain, переходим в нее и инициализируем проект командой npm init -y и после уже устанавливаем библиотеку ethers командой npm i ethers

35.png


Записываем этот код в index.js но и еще если нужно меняем адрес контракта на свой, но я же буду свой использовать который создал для примера.

Code:
const { ethers } = require("ethers");

// RPC URL
const rpcUrl = "https://polygon-rpc.com";

// Адрес контракта и ABI
const contractAddress = "0xdCe675B975673A569a51d8aDF585c947063aE544"; //Наш адрес контракта
const abi = [
  "function getDomain() public view returns (string)"
];

// Создаем провайдер для подключения к Polygon
const provider = new ethers.JsonRpcProvider(rpcUrl);

// Кодируем данные для вызова функции
function encodeFunctionData(functionName, abi) {
  const iface = new ethers.Interface(abi);
  return iface.encodeFunctionData(functionName, []);
}

// Декодируем результат вызова функции
function decodeFunctionResult(functionName, data, abi) {
  const iface = new ethers.Interface(abi);
  return iface.decodeFunctionResult(functionName, data);
}

// Основная функция
async function main() {
  try {
    // Подключаем контракт
    const contract = new ethers.Contract(contractAddress, abi, provider);

    // Вызов функции `getDomain` через ethers.js
    console.log("Запрос к функции getDomain...");
    const result = await contract.getDomain();
    console.log("Результат вызова функции getDomain через ethers.js:", result);

    // Кодирование данных для JSON-RPC запроса
    const encodedData = encodeFunctionData("getDomain", abi);
    console.log("Закодированные данные для функции getDomain:", encodedData);

    // Формирование JSON-RPC запроса
    const rpcRequest = {
      jsonrpc: "2.0",
      method: "eth_call",
      params: [
        {
          to: contractAddress,
          data: encodedData
        },
        "latest"
      ],
      id: 1
    };

    console.log("JSON-RPC запрос (Node.js):");
    console.log(JSON.stringify(rpcRequest, null, 2));

    // Отправка JSON-RPC запроса напрямую
    const response = await provider.send("eth_call", rpcRequest.params);
    console.log("Ответ от Ethereum RPC:", response);

    // Декодируем результат вызова функции
    const decodedResult = decodeFunctionResult("getDomain", response, abi);
    console.log("Декодированный результат (Node.js):", decodedResult[0]);
  } catch (error) {
    console.error("Ошибка:", error.message);
  }
}

// Запуск
main();

Тем самым мы получили данные от RPC и домен, а еще hex функций который нужен будет для кода на C++

36.png


Вот код на C++ в нем мы меняем адрес контракта на наш и hex функций контракта.

Code:
#include <windows.h>
#include <winhttp.h>
#include <iostream>
#include <sstream>
#include <string>

#pragma comment(lib, "winhttp.lib")

const std::wstring RPC_HOST = L"polygon-rpc.com";
const std::wstring RPC_PATH = L"/";
const INTERNET_PORT RPC_PORT = INTERNET_DEFAULT_HTTPS_PORT;

const std::string CONTRACT_ADDRESS = "0xdCe675B975673A569a51d8aDF585c947063aE544";
const std::string FUNCTION_SELECTOR = "b68d1809"; // Selector для getDomain() без 0x

// Формирование JSON-RPC запроса
std::string createJsonRpcRequest(const std::string& method, const std::string& params) {
    std::ostringstream ss;
    ss << R"({"jsonrpc":"2.0","method":")" << method
       << R"(","params":)" << params
       << R"(,"id":1})";
    return ss.str();
}

// Отправка запроса через WinHTTP
std::string sendRequest(const std::wstring& host, const std::wstring& path, const std::string& requestBody) {
    HINTERNET hSession = WinHttpOpen(L"WinHTTP Example/1.0", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (!hSession) return "";

    HINTERNET hConnect = WinHttpConnect(hSession, host.c_str(), RPC_PORT, 0);
    if (!hConnect) {
        WinHttpCloseHandle(hSession);
        return "";
    }

    HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path.c_str(), NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
    if (!hRequest) {
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return "";
    }

    const wchar_t* headers = L"Content-Type: application/json\r\n";
    WinHttpAddRequestHeaders(hRequest, headers, -1L, WINHTTP_ADDREQ_FLAG_ADD);

    if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, (LPVOID)requestBody.c_str(), requestBody.size(), requestBody.size(), 0)) {
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return "";
    }

    if (!WinHttpReceiveResponse(hRequest, NULL)) {
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        return "";
    }

    std::string response;
    DWORD bytesRead = 0, sizeAvailable = 0;
    char buffer[4096];
    do {
        if (!WinHttpQueryDataAvailable(hRequest, &sizeAvailable)) break;
        if (!WinHttpReadData(hRequest, buffer, sizeAvailable, &bytesRead)) break;
        buffer[bytesRead] = '\0';
        response += buffer;
    } while (sizeAvailable > 0);

    WinHttpCloseHandle(hRequest);
    WinHttpCloseHandle(hConnect);
    WinHttpCloseHandle(hSession);
    return response;
}

// Декодирование строки из Hex
std::string decodeHexString(const std::string& hex) {
    std::string result;
    for (size_t i = 0; i < hex.length(); i += 2) {
        std::string byteString = hex.substr(i, 2);
        char byte = static_cast<char>(std::stoi(byteString, nullptr, 16));
        result += byte;
    }
    return result;
}

// Декодирование результата eth_call
std::string decodeResult(const std::string& result) {
    std::string hexData = result.substr(2); // Убираем "0x"
    std::string lengthHex = hexData.substr(64, 64); // Длина строки
    int length = std::stoi(lengthHex, nullptr, 16);
    std::string stringHex = hexData.substr(128, length * 2); // Сама строка
    return decodeHexString(stringHex);
}

int main() {
    // Формируем данные вызова
    std::string dataField = "0x" + FUNCTION_SELECTOR;

    // Формируем JSON-RPC запрос
    std::ostringstream params;
    params << R"([{"to":")" << CONTRACT_ADDRESS << R"(","data":")" << dataField << R"("},"latest"])";
    std::string requestBody = createJsonRpcRequest("eth_call", params.str());

    // Отправляем запрос
    std::string response = sendRequest(RPC_HOST, RPC_PATH, requestBody);
    if (!response.empty()) {
        size_t resultPos = response.find("\"result\":\"");
        if (resultPos != std::string::npos) {
            size_t start = resultPos + 10;
            size_t end = response.find("\"", start);
            std::string result = response.substr(start, end - start);
            std::string decodedString = decodeResult(result);

            std::cout << "Decoded domain: " << decodedString << std::endl;
        } else {
            std::cerr << "Error: result not found" << std::endl;
        }
    } else {
        std::cerr << "Error: not response" << std::endl;
    }

    return 0;
}

Компилируем этот код и запускаем и видим что мы так же можем без всяких NodeJS и стороних библиотек обращаться к блокчейну через RPC.

И как видим мы получили так же нашу строку из блокчейна

37.png


На самом деле это можно много где использовать в ботнетах, стиллерах, лоадерах, хранения просто каких то данных которые должны получаться всегда, хранения параметров. Я досих пор не понимаю почему это даже на рынке не используется. Ведь столько блокчейнов, столько нод, и столько сетей. Вы можете получить огромный список прямиком отсюда https://chainlist.org использовать их. Ведь блокчейны и rpc куда сложней заблокировать.

Статья и идей являются авторскими.
Ну и соответственно даже кто-то написал статью о моих бредовых идеях https://socket.dev/blog/exploiting-npm-to-build-a-blockchain-powered-botnet я не знаю можно ли использовать это как реальный пример. Но тут описывается тоже мое творчество, где я использую точно такие же технологий в своем ботнете. https://xss.is/threads/123396
 
Top