ANTICHAT — форум по информационной безопасности, OSINT и технологиям
ANTICHAT — русскоязычное сообщество по безопасности, OSINT и программированию.
Форум ранее работал на доменах antichat.ru, antichat.com и antichat.club,
и теперь снова доступен на новом адресе —
forum.antichat.xyz.
Форум восстановлен и продолжает развитие: доступны архивные темы, добавляются новые обсуждения и материалы.
⚠️ Старые аккаунты восстановить невозможно — необходимо зарегистрироваться заново.
 |

09.02.2013, 17:04
|
|
Участник форума
Регистрация: 10.01.2013
Сообщений: 100
Провел на форуме: 18665
Репутация:
27
|
|
Интро
Сейчас актуально писать мелкий говнософт для разных социальных сетей. Обычно начинающие быдлокодеры пишут подобное с помощью процедурного подхода и если проект более-менее сложный начинают захлебываться в глобальных переменных и значениях процедур и функций. Я хочу продемонстрировать ООП подход и его приимущества. В статье будет ценен не код программы (скорее он то только для устрашения), а способ мышления при использовании ООП подхода.
Этап 1
ТЗ: Написать дампер сообщений социальной сети ВКонтакте с возможностью дампить переписку как по SID так и по логину/паролю.
Оговорюсь заранее, что при написании софта, я встретил много подводных камней, которые значительно усиливали запутанность программы. Я не стал вдаваться в безупречную реализацию, тк, пишу для демонстрации объектно ориентированного метода, но все-же я пометил комментариями участки кода, требующие доработки.
Что нам понадобится?
1) Язык поддерживающий ООП (Я пишу на Дэльфи)
2) Сниффер сетевого трафика (работать будем в основном с http поэтому httpAnalyzer вполне устроит).
Этап 2
По ООП есть куча разной литературы. Я попробую максимально доступно объяснить всю суть на конкретном примере. Итак, исходя из ТЗ мне нужно создать класс объекта, который смог бы проделывать определенные операции на сайте. Для начала происнифаем сайт и разберемся в алгоритмах запросов. Об этом я вообще писать не буду. По окончании у нас будет алгоритм примерно такой:
1 - Авторизуемся
2 - парсим корешей
3 - парсим месаги
Создаем новый класс объекта . Коротко немного теории : каждый класс что-то знает и что-то умеет делать. То что класс знает называется полями класса (если угодно переменные), а то что он умеет - называется методами класса (если угодно процедуры и функции)... В самомо начале проэктируется внешний вид класса (что знает умеет), а потом реализация (как он умеет). Чтож, давайте попробуем определиться, что необходимо знать и уметь нашему классу...
Знать: авторизационные данные (логин пароль), SID пользователя, количество друзей пользователя, список ID друзей, и он должен взаимодействовать с сетью по протоколу http, поэтому будет разумно запихнуть в него объект типа TidHTTP. Можно продолжать, но в учебных целях притормозим
Уметь: Авторизоваться, Парсить ID друзей, Парсить сообщения.
Также, каждый объект класса будет инициализирован под средством метода конструктора Create () . В нем удобно создавать объекту все условия для комфортной работы (создавать файлы/ соединяться с БД/ задавать настройки по умолчанию других объектов.)
Код:
//---------------Class for work with Vk.com---------
type
TVKDump = Class
Login, Password : String; // Данные для авторизации
Link : String; // Нужная шняга
SID : String; // Значение remixsid в кукисах
HTTP : TidHTTP; // Для работы с нетом
FriendList : TStringList; // Список ИДшников друзей
FriendCount : Integer; //Количество друзей
function ParseMessage (FriendID : String) : Boolean; //Вытаскиваем сообщения со скритов
function Authorize () : Boolean; //Авторизация
function ParseFriends () : Integer; //Парсим ИД корешей
constructor Create (); // Чето создаем и настраиваем
end;
//-------------------------------------------------
Этап 3
Вот собственно и все! Осталось всего ничего описать реализацию каждой функции . Здесь в подробности я вдаваться не буду, просто выложу свой быдлокод .... Там в основном парсинг и танци с бубном вокруг запросов к ВК.
Код:
//------------function parse messages with a script--------------------------
function TVKDump.ParseMessage (FriendID : String) : Boolean;
var Data, MsgList : TStringList;
Name, Mess, tmp : String;
i, j : Integer;
begin
Result := True;
try
try
HTTP.AllowCookies := False;
HTTP.Request.CustomHeaders.Clear;
HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID);
MsgList := TStringList.Create;
Data := TStringList.Create;
for j := 0 to 5 do
begin
Data.Clear;
Data.Add('act=a_history');
Data.Add('al=1');
// offset - параметр смещения сообщений, можно пробовать подставлять
Data.Add('offset=' + IntTostr(j*50));
Data.Add('peer=' + FriendID);
Data.Add('whole=0');
tmp := HTTP.Post('http://vk.com/al_im.php', Data);
HTTP.AllowCookies := True;
Sleep(200);
for i := 1 to 100 do
begin
Delete(tmp, 1, Pos('class="mem_link" target="_blank">', tmp) + 32);
Name := Copy(tmp, 1, Pos('', tmp) - 1);
Delete(tmp, 1, Pos('', tmp) + 5);
Mess := Copy(tmp,1, Pos('', tmp) - 1);
MsgList.Add(Name + ':' +Mess);
end;
end;
MsgList.SaveToFile(FriendID + '_damp.txt');
except
Result := False;
end;
finally
MsgList.Free;
Data.Free;
end;
end;
//----------------------------------------------------------
//-----------------authorization-----------------------------
function TVKDump.Authorize () : Boolean;
//---------------parse link necessary to request-------------
function ParseLink ( Page : String) : Boolean;
begin
Result := false;
Delete(page,1,Pos('action=',page)+15);
link := Copy(page,1,pos('>',page)-2);
if link <> ''
then
begin
Link := 'http://' + Link;
Result := true;
end;
end;
//--------------------------------------------------------------
var Data:TStringList;
tmp: String;
begin
Result := False;
try
try
Data := TStringList.Create;
Data.Add('email='+Login);
Data.Add('pass='+Password);
//----------- реализация быдлятская, но нет времени делать норм -----------------------
ParseLink(HTTP.Get('http://m.vk.com/login'));
HTTP.HandleRedirects := False;
HTTP.AllowCookies := False;
try
HTTP.Post(Link, Data);
except
try
HTTP.Get(HTTP.Response.Location);
except
tmp := HTTP.Response.RawHeaders.Text;
Delete(tmp, 1, Pos('remixsid=',tmp) + 8);
SID :=Copy( tmp,1 ,Pos('expires=',tmp)-3);
end;
end;
//--------------------------------------------------------------------------------------
HTTP.HandleRedirects := True;
HTTP.AllowCookies := True;
tmp := HTTP.Get(HTTP.Response.Location);
if Pos('logout',tmp) <> 0
then Result := True;
except
Result := False;
end;
finally
Data.Free;
end;
end;
//-------------parsing function friends ID's -----------------------
// парсит вроде первых 20 чтобы парсило всех нужно допиливать
function TVKDump.ParseFriends () : Integer;
var tmp: String;
i:integer;
begin
HTTP.AllowCookies := False;
HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID);
tmp := HTTP.Get('http://m.vk.com/friends');
HTTP.AllowCookies := True;
Delete(tmp,1,Pos(' ',tmp)+4);
FriendCount :=StrToInt(Copy(tmp,1,Pos('',tmp)-1));
for i:=1 to 18 do
begin
Delete(tmp,1,Pos('',tmp)-1));
end;
FriendList.SaveToFile('friends.txt');
end;
//-----------------------------------------------------------
//-------------- Созаём нужные объекты и настраиваем HTTP ----------------
constructor TVKDump.Create ();
begin
FriendList := TStringList.Create;
HTTP := TIdHTTP.Create(nil);
HTTP.HandleRedirects := True;
with HTTP.Request do
begin
UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0';
ContentType := 'application/x-www-form-urlencoded';
AcceptLanguage := 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3';
end;
end;
Этап 4
На 4 этапе необходимо создать объект нашего класса. Для этого его нужно инициализировать методом Create (). Также описывается поведение объекта и взаимодействие с другими компонентами программы. Дабы немного поизвращаться я не стал делать GUI. Программа получает необходимые данные ввиде параметров командной строки, а результаты пишет в файлы.
Код:
Var
VK : TVKDump;
i : Integer;
begin
VK := TVKDump.Create ();
//-----------если без запущена параметров, тогда выходим -----------------
if ((Paramstr(1) = '') and (Paramstr(2) = '')) then
begin
Exit;
end;
//----------возможность указать SID---------------------------------------
if ((Paramstr(1) = '0') and (Paramstr(2) = '0'))
then
begin
VK.SID := Paramstr(3);
VK.ParseFriends();
for i := 1 to 3 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей
VK.ParseMessage(VK.FriendList[i]);
end
else //-----------иначе указываем логин и пароль------------------------
begin
VK.Login := Paramstr(1);
VK.Password := Paramstr(2);
VK.Authorize;
VK.ParseFriends();
for i := 1 to 5 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей
VK.ParseMessage(VK.FriendList[i]);
end;
end.
Чтобы автоизоваться с помощью логина и пароля нужно в консольке набрать
Сообщение от None
D:\Delphi Projects\7\vkdumper\VKDump>VK_Dumper.exe pass login
А если по SID:
Сообщение от None
D:\Delphi Projects\7\vkdumper\VKDump>VK_Dumper.exe 0 0 6dbed7eb6eb7e7d7bd6be76bd 76be76e6db7e6b7e67eb7b
Оутро
Моя программа является еще сырым приложением и используется в качестве наглядности, но если есть необходимость и руки = тру, то и интерфейс можно прикрутить и потоки и функции поправить...
Подобным образом создаются проекты побольше со сложной структурой. Мир ООП очень богат и при разумном использовании очень помогает упростить жизнь разработчику как професионального уровня так и мелкому фрилансеру. Надеюсь кому-то пригодиться. Спасибо за внимание. BlackIce.
Привожу код всего приложения:
Код:
program VK_Dumper;
Uses
IdHttp,IdCookieManager,Dialogs, IdHTTPHeaderInfo, Classes, SysUtils;
//---------------Class for work with Vk.com---------
type
TVKDump = Class
Login, Password : String; // Данные для авторизации
Link : String; // Нужная шняга
SID : String; // Значение remixsid в кукисах
HTTP : TidHTTP; // Для работы с нетом
FriendList : TStringList; // Список ИДшников друзей
FriendCount : Integer; //Количество друзей
function ParseMessage (FriendID : String) : Boolean; //Вытаскиваем сообщения со скритов
function Authorize () : Boolean; //Авторизация
function ParseFriends () : Integer; //Парсим ИД корешей
constructor Create (); // Чето создаем и настраиваем
end;
//-------------------------------------------------
//------------function parse messages with a script--------------------------
function TVKDump.ParseMessage (FriendID : String) : Boolean;
var Data, MsgList : TStringList;
Name, Mess, tmp : String;
i, j : Integer;
begin
Result := True;
try
try
HTTP.AllowCookies := False;
HTTP.Request.CustomHeaders.Clear;
HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID);
MsgList := TStringList.Create;
Data := TStringList.Create;
for j := 0 to 5 do
begin
Data.Clear;
Data.Add('act=a_history');
Data.Add('al=1');
// offset - параметр смещения сообщений, можно пробовать подставлять
Data.Add('offset=' + IntTostr(j*50));
Data.Add('peer=' + FriendID);
Data.Add('whole=0');
tmp := HTTP.Post('http://vk.com/al_im.php', Data);
HTTP.AllowCookies := True;
Sleep(200);
for i := 1 to 100 do
begin
Delete(tmp, 1, Pos('class="mem_link" target="_blank">', tmp) + 32);
Name := Copy(tmp, 1, Pos('', tmp) - 1);
Delete(tmp, 1, Pos('', tmp) + 5);
Mess := Copy(tmp,1, Pos('', tmp) - 1);
MsgList.Add(Name + ':' +Mess);
end;
end;
MsgList.SaveToFile(FriendID + '_damp.txt');
except
Result := False;
end;
finally
MsgList.Free;
Data.Free;
end;
end;
//----------------------------------------------------------
//-----------------authorization-----------------------------
function TVKDump.Authorize () : Boolean;
//---------------parse link necessary to request-------------
function ParseLink ( Page : String) : Boolean;
begin
Result := false;
Delete(page,1,Pos('action=',page)+15);
link := Copy(page,1,pos('>',page)-2);
if link <> ''
then
begin
Link := 'http://' + Link;
Result := true;
end;
end;
//--------------------------------------------------------------
var Data:TStringList;
tmp: String;
begin
Result := False;
try
try
Data := TStringList.Create;
Data.Add('email='+Login);
Data.Add('pass='+Password);
//----------- реализация быдлятская, но нет времени делать норм -----------------------
ParseLink(HTTP.Get('http://m.vk.com/login'));
HTTP.HandleRedirects := False;
HTTP.AllowCookies := False;
try
HTTP.Post(Link, Data);
except
try
HTTP.Get(HTTP.Response.Location);
except
tmp := HTTP.Response.RawHeaders.Text;
Delete(tmp, 1, Pos('remixsid=',tmp) + 8);
SID :=Copy( tmp,1 ,Pos('expires=',tmp)-3);
end;
end;
//--------------------------------------------------------------------------------------
HTTP.HandleRedirects := True;
HTTP.AllowCookies := True;
tmp := HTTP.Get(HTTP.Response.Location);
if Pos('logout',tmp) <> 0
then Result := True;
except
Result := False;
end;
finally
Data.Free;
end;
end;
//-------------parsing function friends ID's -----------------------
// парсит вроде первых 20 чтобы парсило всех нужно допиливать
function TVKDump.ParseFriends () : Integer;
var tmp: String;
i:integer;
begin
HTTP.AllowCookies := False;
HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID);
tmp := HTTP.Get('http://m.vk.com/friends');
HTTP.AllowCookies := True;
Delete(tmp,1,Pos(' ',tmp)+4);
FriendCount :=StrToInt(Copy(tmp,1,Pos('',tmp)-1));
for i:=1 to 18 do
begin
Delete(tmp,1,Pos('',tmp)-1));
end;
FriendList.SaveToFile('friends.txt');
end;
//-----------------------------------------------------------
//-------------- Созаём нужные объекты и настраиваем HTTP ----------------
constructor TVKDump.Create ();
begin
FriendList := TStringList.Create;
HTTP := TIdHTTP.Create(nil);
HTTP.HandleRedirects := True;
with HTTP.Request do
begin
UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0';
ContentType := 'application/x-www-form-urlencoded';
AcceptLanguage := 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3';
end;
end;
Var
VK : TVKDump;
i : Integer;
begin
VK := TVKDump.Create ();
//-----------если без запущена параметров, тогда выходим -----------------
if ((Paramstr(1) = '') and (Paramstr(2) = '')) then
begin
Exit;
end;
//----------возможность указать SID---------------------------------------
if ((Paramstr(1) = '0') and (Paramstr(2) = '0'))
then
begin
VK.SID := Paramstr(3);
VK.ParseFriends();
for i := 1 to 3 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей
VK.ParseMessage(VK.FriendList[i]);
end
else //-----------иначе указываем логин и пароль------------------------
begin
VK.Login := Paramstr(1);
VK.Password := Paramstr(2);
VK.Authorize;
VK.ParseFriends();
for i := 1 to 5 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей
VK.ParseMessage(VK.FriendList[i]);
end;
end.
|
|
|

09.02.2013, 19:43
|
|
Познавший АНТИЧАТ
Регистрация: 23.08.2007
Сообщений: 1,237
Провел на форуме: 18127311
Репутация:
1676
|
|
Сообщение от None
пишут подобное с помощью процедурного подхода и если проект более-менее сложный начинают захлебываться в глобальных переменных и значениях процедур и функций
Сообщение от None
способ мышления при использовании ООП подхода.
Сообщение от None
Login, Password : String; // Данные для авторизации
Link : String; // Нужная шняга
SID : String; // Значение remixsid в кукисах
HTTP : TidHTTP; // Для работы с нетом
FriendList : TStringList; // Список ИДшников друзей
FriendCount : Integer; //Количество друзей
function ParseMessage (FriendID : String) : Boolean; //Вытаскиваем сообщения со скритов
function Authorize () : Boolean; //Авторизация
function ParseFriends () : Integer; //Парсим ИД корешей
constructor Create (); // Чето создаем и настраиваем
Т.е. берем класс, фигачим в нем всё то же разрозненное нечто, что в теории существовало бы в виде отдельных процедур и пары глобальных переменных и называем это ООП подходом, который обладает "приимуществами" в виде упрощенной поддержки ?
|
|
|

09.02.2013, 20:28
|
|
Познавший АНТИЧАТ
Регистрация: 30.09.2010
Сообщений: 1,168
Провел на форуме: 257934
Репутация:
288
|
|
Сообщение от Kaimi
Т.е. берем класс, фигачим в нем всё то же разрозненное нечто, что в теории существовало бы в виде отдельных процедур и пары глобальных переменных и называем это ООП подходом, который обладает "приимуществами" в виде упрощенной поддержки ?
конечно это лишь просто пример работы с классами,и тут можно было бы спокойно обойтись процедурами(мне и самому ближе процедурно-функциональное программирование,ну не люблю я классы ёкрмн,писать по меньшей мере,юзать готовые-да ).Вот банальный пример, когда лучше применить класс:http://mrkto.com/oop_php_class_function/
а так ТС молодец,и идея прикольна
|
|
|

09.02.2013, 20:41
|
|
Постоянный
Регистрация: 06.11.2006
Сообщений: 865
Провел на форуме: 1977708
Репутация:
208
|
|
Кстати, давно искал такой софт )) чтоб дампить переписку из акков, и в офф-лайне спокойно читать/разобрать.
Можете дать рабочую прогу?
|
|
|

09.02.2013, 20:48
|
|
Новичок
Регистрация: 01.03.2007
Сообщений: 2
Провел на форуме: 3478
Репутация:
0
|
|
Поправте меня, но на статью и близко не тянет. Что бы представить модель ООП не хватит написать только один класс-обертку. Суть ООП всё таки это взаимодействие между различными обьектами, а не работа с одним единственным классом-обьектом.
Как пример обьекта - годится, но не как представление модели ООП.
|
|
|

09.02.2013, 21:07
|
|
Участник форума
Регистрация: 10.01.2013
Сообщений: 100
Провел на форуме: 18665
Репутация:
27
|
|
2 Kaimi, DX вы же делфи не знаете?
На статью мало тянет, согласен. Можно было показать примеры наследования и взаимодействия с классами. Но так как сам только практикую, то и писал для себеподобных. Вс-еже, даже такой подход упрощает разработку и разбивает создание на 2 этапа: проэктирование класса, где кодер на абстрактном уровне разрабатывает структуру будущей программы и реализацию. За критику благодарю, буду стараться больше работать в данной сфере и писать более годные статьи.
Линк на файл : http://www.sendspace.com/file/dykm4i
|
|
|

10.02.2013, 01:26
|
|
Новичок
Регистрация: 21.06.2005
Сообщений: 1
Провел на форуме: 0
Репутация:
0
|
|
В статье хорошо продемонстрирован антипаттерн "God object". Слишком много функционала возложено на один объект. Не обязательно знать делфи, чтобы не заметить такой ошибки.
Главная задача ООП - закрывать для изменения существующий и отлаженный код, и предоставить возможность добавлять к нему новую функциональность. За счет этого обеспечивается повторное использование.
В данном случае нет смысла в классе TVKDump; с тем же успехом можно было бы сделать все это модулем (delphi unit), а поля - просто переменными этого модуля.
Получение страницы с веб-сервера vk.com - в принципе задача для отдельного класса. Ведь можно грузить страницы как напрямую, так и через прокси или цепочки прокси. Если все это запихнуть в один класс, никакой выгоды от ООП не будет, т.к. такой код сложнее выдрать для использования где-либо еще. Страница может загружаться не полностью, а частями (для уменьшения трафика) + списки друзей и прочее могут быть реализованы на сайте постранично (следовательно для каждой страницы требуется отдельный запрос).
Аутентификацию тоже следует вынести в отдельный класс, зависящий от соединения с vk (а не просто от сферического TidHTTP, поскольку это внешняя зависимость). Предположим, что вконтакте спалил наш "многопоточный сканер" и решил показать каптчу, на абсолютно произвольном запросе. Теперь придется в код класса внедрять еще и каптчу. Это значит, что надо интегрировать все это дело с antigate и как альтернативу предоставить возможность вбивать их вручную.
А если делаем многопоточность, там может возникнуть задача равномерно размыть запросы по всем проксям, с учетом числа запросов и переданного трафика. Тогда потребуется вести учет по трафику и числу запросов каждой прокси - еще один класс, который будет решать, через какой прокси должен быть отправлен запрос в этот раз. Плюс обход всяких сбоев при работе с прокси, необходимость повторения запроса в случае падения коннекта - вообщем, еще много различных проблем можно найти.
Особого проектирования в этой статье не усматривается. Обычный код, который пишут на скорую руку. Все, что здесь есть ООПшного - только использование дельфийских классов и компонентов путем композиции.
P.S. Для программы уровня "написал и забыл" данный код хорошо годится, главное, чтобы работало. А поддерживать такое не пожелаю никому
|
|
|

10.02.2013, 03:51
|
|
Постоянный
Регистрация: 13.12.2008
Сообщений: 354
Провел на форуме: 1747641
Репутация:
175
|
|
Сообщение от BlackIce
2 Kaimi, DX вы же делфи не знаете?
То есть ты хочешь сказать, что знаешь Delphi? Или даже ООП?
ТС, прочитай про атрибуты видимости методов, переменных, про свойства. Так как ты написал пишут только начинающие быдлокодеры. Ко всему этому ты еще создаешь объекты в конструкторе, но нигде их не освобождаешь. Зачем писать статью с такими ошибками? Чтобы сделать свой членский взнос в литературу для быдлокодеров?
|
|
|
|
 |
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|