PDA

Просмотр полной версии : хуки для самых маленьких


destructor
29.01.2010, 23:01
участились случаи ногебания icq по вопросу "как перехватить функцию"
в гугл слал, в лес слал, в бабруйск слал, всеравно возвращаются с вопросами :(
незнаю куда еще можно послать... поэтому решил описать "своими словами" чтобы слать сюда.

существует стопицот способов перехвата, у всех есть свои плюсы\минусы
опишу простейший, называется "сплайсинг"


0)
есть приложение, компилить не надо,
просто качаем\запускаем\пробуем вложение 918,
исходник такой:
#include <Windows.h>

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
LoadLibrary(L"tabletka.dll"); //чтобы не засирать пример, "инжектить" лекарство будем так.

MessageBoxW(0,L"нажмите ок чтобы ботить, тока тсссс.....",L"ncsoft(c)",MB_ICONWARNING);
MessageBoxW(0,L"ботоводы, горите в аду!",L"угнова(c)",MB_ICONERROR | IDCANCEL );
return 0;
};
как мы видим, данная программа имеет некоторые недостатки,
а именно, наглые, неполиткорректные высказывания.
а так делать незя.

1)
лезем сюда _http://msdn.microsoft.com/ изучаем нужную нам функцию (ну или хотябы код в заголовках),
она выглядит так: int __stdcall MessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
находится тут: Minimum DLL Version user32.dll

2)
теперь нужно научится ее вызывать хитрым способом (или изготовление функции трамплина),
делаем простой пример:
DWORD addr=0;
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
__asm jmp [addr];
};
main()
{
DWORD target = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"messageboxW");
addr = target + 5; //прыгать мы будем не в "начало" функции, а на пять байт дальше.
jmpMessageBoxW(0,L"текст",L"текст", UINT uType);
};
//компилируем, убеждаемся что такая программа будет работать.

мы пропускаем часть оригинального кода, а ошибок всеравно нету, обьясняю почему.
в отладчике это будет выглядить так:
test.exe:10001770 jmpMessageBoxW:
test.exe:10001770 push ebp // выполняется, этот код дописал нам компилятор
test.exe:10001771 mov ebp, esp // выполняется, этот код дописал нам компилятор
test.exe:10001773 jmp ds:addr // наш джамп
test.exe:10001779 pop ebp // сюда мы никогда не попадем
test.exe:1000177A retn 10h
..
user32.dll:7E3B6534 MessageBoxW:
user32.dll:7E3B6534 mov edi, edi //пропускаем, бессмысленная инструкция, типа x=x, так захотели в майкрософт.
user32.dll:7E3B6536 push ebp //пропускаем
user32.dll:7E3B6537 mov ebp, esp //пропускаем
user32.dll:7E3B6539 cmp ds:dword_7E3C14BC, 0 //<<--мы прыгаем сюда
user32.dll:7E3B6540 jz short loc_7E3B6566

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

значит если изуродовать первые 5 байт оригинальной функции, мы всеравно сможем ее вызвать.


3)
придумаем "функцию фильтр_обработчик"
int __stdcall hookMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
int result = 0;
if(lstrcmpW(lpCaption,L"угнова(c)"))
{
//если в заголовке окна есть нецензурное слово, вызываем оригинал со своими параметрами
result = jmpMessageBoxW(hWnd, L"слава роботам!!! убить всех человеков!!", L"какой то робот(с)", MB_ICONINFORMATION);
}
else
{
//если в заголове чтото другое, ничего не трогаем, вызываем оригинал
result = jmpMessageBoxW(hWnd, lpText, lpCaption, uType);
};
return result;
};


продолжение ниже

destructor
29.01.2010, 23:03
4)
собственно сам "сплайсинг"
идея заключается в том чтобы в начале оригинальной функции записать "jmp куда_нам_надо" и тогда поциэнт будет вызывать "то_что_нам_надо"

код инструкции jmp xxxxx выглядит так:
1байт опкод 0xe9 + 4 байта смещение(какраз 5 байт, тоже "совпадение"),
"смещение" означает сколько байт нам надо перепрыгнуть,
если нам надо прыгнуть на 500 байт вперед, пишем туда 500,
если нам 300 байт назад пишем туда -300.
вычеслить нужное нам значение нужно так:
v = targetaddr - posjmpaddr;
targetaddr, адрес куда надо прыгнуть (в нашем случае это адрес hookMessageBoxW)
posjmpaddr, адрес инструкции jmp + ее размер (+5 байт).

тоесть,
//делаем доступной память для записи.
target = (DWORD)GetProcAddress(LoadLibrary("user32.dll"),"MessageBoxW");
VirtualProtect((void*)target,PAGE_EXECUTE_READWRIT E,10,&oldprotect);
//вычисляем адрес куда будем писать опкод:
BYTE * pE9 = (BYTE*)target;
//вычисляем адрес, куда будем писать смещение
DWORD * pofset = (DWORD*)(pE9+1);
//пишем в память:
*pE9 = 0xe9;
*pofset = (DWORD)hookMessageBoxW - (target+5);


5)
собираем в кучу написанное выше и оформляем в виде длл:
#include <Windows.h>

//трамлин
DWORD addr;
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
__asm jmp [addr];
};
//обработчик
int __stdcall hookMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
int result = 0;
if(lstrcmpW(lpCaption,L"угнова(c)")==0)
{
//если в заголовке окна есть нецензурное слово, вызываем оригинал со своими параметрами
result = jmpMessageBoxW(hWnd, L"слава роботам!!! убить всех человеков!!", L"какойто робот(с)", MB_ICONINFORMATION);
}
else
{
//если в заголове чтото другое, ничего не трогаем, вызываем оригинал
result = jmpMessageBoxW(hWnd, lpText, lpCaption, uType);
};
return result;
};

BOOL WINAPI DllMain( HINSTANCE hModule,DWORD Reason, LPVOID lpReserved )
{
DWORD target;
DWORD oldprotect;
BYTE * pE9;
DWORD * pofset;

switch(Reason)
{
case DLL_PROCESS_ATTACH:

target = (DWORD)GetProcAddress(GetModuleHandleW(L"user32.dll"),"MessageBoxW");
if(!VirtualProtect((void*)target,10,PAGE_EXECUTE_R EADWRITE,&oldprotect))return FALSE; // а вдруг?

//настраиваем трамплин
addr = target + 5; //прыгать мы будем не в "начало" функции, а на пять байт дальше.

//правим оригинал, в начало записываем джамп на наш обработчик
pE9 = (BYTE*)target;
pofset = (DWORD*)(pE9+1);
*pE9 = 0xe9;
*pofset = (DWORD)hookMessageBoxW - (target+5);
break;

case DLL_PROCESS_DETACH:
//тут выгрузка длл
//надо восстановить оригинальный код функции,
//но нам по*уй, такой херней страдают только в конец задроченные ботаны,
//а все чоткие праграмисты знают что "меньше кода = меньше багов"
break;
};
return TRUE;
};
жмем компилить, должна получится 919
теперь если рядом с нашим поциэнтом положить таблетку, мы сможем наблюдать чудесное исцеление.

какашками кидацо незя, т.к. это часный\стерильный случай

xkor
30.01.2010, 00:29
destructor, то что первые 5 байт совпали в этом примере эт конечно хорошо, но это довольно редкий случай и охрененно упрощающий код притом. А вот если первые 5 байт не совпадают, то начинается веселье, надо переместить первые инструкции (естественно целое количество инструкций) которые заполняют первые 5 или больше байт куданить себе в массив и ещё дописать в массив джамп на оригинальную функцию (со смещением равным количеству списанных из начала байт), оформить этот массив как имеющий возможность быть выполненным (ну VirtualProtect'ом обработать опять же), а вот уже дальше записать на место первых 5 байт оригинала джамп, ну и в своей функции вместо оригинала уже вызывать не оригинал со смещением, а наш массив.
ЗЫ и эт ещё хорошо если мы можем функцию перехватываемую в дизассемблере посмотреть и посчитать сколько байт занимает целое число команд в начале, а если не можем то надо ещё как то программно это определять...

finomen
30.01.2010, 00:47
по теме [W A S M . R U] СТАТЬИ ? Секреты Win32 ? Перехват API функций в Windows NT (часть 1). Основы перехвата. (http://www.wasm.ru/article.php?article=apihook_1)

SeregaZ
30.01.2010, 01:08
а есть идея как экспорер перехватывать? точней html контент. потом перекраивать как надо - и подсовывать эксплореру как нивчем небывало?
сейчас перед запуском своей программы я ставлю в файлик hosts этот сайт, и "редиректом" на 127.0.0.1 - где, собственно, поднимается маленький веб сервер. браузер в результате не замечает и работает, думая что все нормально... вот хотелось бы, пускай вирусоподобно, подменять этот адрес в самом эксплорере, без редактирования файла hosts. и при этом чтобы сохранялись всякие keep alive соединения будь то форум, веб аська, или чат.

finomen
30.01.2010, 01:17
1) напиши проксик и запихай в настройки эксплорера
2) хук на send\recv (см. исходники l2ph)
3) драйвер-фильтр сетевого интерфейса

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

SeregaZ
30.01.2010, 16:02
хороший пример proxomitron. работает как прокси сервер и фильтр. туда засунул по шаблону к примеру рекламу бегуна - и на всех сайтах, где он её встретит - отрезает.
но там все фильтры открыты и нужно браузер настраивать на работу как с прокси. а мне хотелось бы эти самые фильтры спрятать, а все настройки автоматизировать. притом proxomitron, и современные её аналоги типа proximodo - уже не развиваются, закрыты сто лет назад. и основная проблема подвисает чуть чуть.
поэтому хотел бы сделать нечто вроди плагина для IE, который бы занимался этим самым редиректом по началу, и как развитие идеи - перекраиванием по шаблону как proxomitron.

destructor
30.01.2010, 17:16
но это довольно редкий случай такие совпадения это довольно частый случай,
во всех основных винапи майкрософт специально дали нам эти 5 байт (добавли 2 nop в начале),
и охрененно упрощающий код притом.ну тут какбы ключевая фраза




или больше байт куданить себе в массив и ещё дописать в массив джамп на оригинальную функцию
я "массив" готовлю так:
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
__asm nop
__asm nop
__asm nop
__asm nop
__asm nop
__asm nop
__asm nop
__asm jmp [addr];
};

адрес jmpMessageBoxW это начало "массива",
потом просто делаю копипасту нужного количества инструкций:
memcpy(jmpMessageBoxW,MessageBoxW, X);
и все, код не сильно усложняется

GoldFinch
08.05.2010, 23:43
Для реальных ситуаций, надо либо смотреть какие инструкции в начале функции и прописывать их в jmpMessageBoxW() руками,
либо использовать дизассемблер длин инструкций и определять длину и тип копируемых инструкций с его помощью.

Также надо учитывать что когда в начало функции пишется "jmp hook", то если какой-то поток в это время исполняет код в этом месте - прога упадет, по этому либо надо быть уверенным что никакие потоки эту часть кода исполнять не будут, либо замораживать все потоки, проверять это и если надо - переносить eip на новый код.

Дизассемблер длин инструкций, адаптированный для VC++ можно посмотреть тут
http://code.google.com/p/nabla-kb/source/browse/system/rce/cpp_rce_tools/rce/hook.cpp
(функция instruction_length, пропустить сложно; остальной код в этом сорце работает только частично)

Yegor
09.05.2010, 23:26
Кто то поделится секретом, какую функцию в л2 (руоф) хукать для того чтобы можно было получить копию готового, расшифрованного пакета?

xkor
10.05.2010, 00:24
Yegor, смотря в каком направлении)

Yegor
10.05.2010, 00:39
Да пока интересует чтение, но было бы интересно и запись.
Сейчас смотрю в сторону gameShieldDll.dll, через нее идет обмен пакетами с сервером? До нее должна быть стандартная шифрация?

xkor
10.05.2010, 17:35
Yegor, причем тут чтение и запись?, чтение и запись чего и во что??
я спрашивал пакеты какого направления тебя интересуют, от сервера клиенту или наоборот?

Yegor
10.05.2010, 19:17
xkor, сейчас интересует от сервера клиенту

xkor
10.05.2010, 19:21
Yegor, тогда AddNetworkQueue

Yegor
14.05.2010, 06:09
Как то можно вычислить какие параметры передаются при вызове определнной функции? Или только через дизасемблер?
Может в какой нибудь регистр заносится количество параметров помещенных в стек.

xkor
14.05.2010, 15:57
Yegor, только через дизассемблер)

Yegor
14.05.2010, 16:35
Вобщем перехватил фукнцию AddNetworkQueue но не могу разобратся до конца.

Вот моя функция которая вызываетс клиентом вместо AddNetworkQueue

procedure ConnectHookProc; register;
begin
asm
pushad;
end;
//полезный код
..............

asm
popad;
jmp [ConnectNextHook]; // переход в ориг-ую функцию AddNetworkQueue
end;
end;



Пока что никакие параметры функции не трогаю. такая связка работает без проблемм, но если между 1 и 2 ассемблерным блоком добавить вызов своей функции то л2 вылетает сразу с критом. Вроде все регистры нужные регистры я сохранил, проверял указатель на стек восстанавливается корректно. Что еще я не учел?

Yegor
14.05.2010, 17:49
Morfik, теперь и без каких либо сторонних вызовов критует :(

Prixmegently
14.05.2010, 19:56
Как то можно вычислить какие параметры передаются при вызове определнной функции? Или только через дизасемблер?
Может в какой нибудь регистр заносится количество параметров помещенных в стек.

Если это экспортная функция из ла2 то можно расшифровать имя с помощью undname и получить кол-во и типы параметров.

Yegor
14.05.2010, 19:59
Спасибо, пакеты от сервера принял.

Yegor
17.05.2010, 00:41
xkor, с приемом пакета полностью разобрался все работает.
Теперь нужны идеи по поводу отправки. В соседней теме "Радар своими руками" в этом посте http://coderx.ru/showpost.php?p=62580&postcount=53 кое что указано, но остается не ясным как на самом деле называется функция, которую murc вызывает в своих функциях L2MemoryAlloc. Там указаны прямые адреcа а названий нет :(

xkor
17.05.2010, 05:12
Yegor, а как обзовёшь так и будет называться, эта функция не экспортируется так что название её в клиенте не фигурирует нигде...
на неё по идее должен быть указатель в какой нить таблице методов, но я не нашел, правда и не искал особо ибо мне проще другую функцию (тоже не экспортируемую, но где её адрес взять я знаю) в середине хукнуть чтоб она всё что мне надо вызвала за меня.

Prixmegently
18.05.2010, 22:10
Отправлять функцией

VOID __cdecl SendPacket (VOID *This, CHAR *Format, ...)

Адрес функции в engine.dll на руофе сейчас - 0x203B0B90
Вообще можно найти рядом с вызовом ws2_32.send()

Да и murc отправлял пакет клиенту, т.е. добавлял свой пакет в очередь. Наверное тебе нужно отправку пакета на сервер, о которой я написал выше.

Кстати говоря фрост сейчас палит сплайсинг кажись.

Yegor
19.05.2010, 01:40
Prixmegently,
Я как раз токо что вычислил адрес функции и глянул в тему, а ты уже написал. зато я знаю как считать терь :).



И поводу параметров функции SendPacket, нужно опять таки выделять дето память и передавать указатели на необходимые структуры?

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

Хотя смотрю все таки все в ней
engine.dll:203B0CD8 call near ptr ws2_32_send

xkor
19.05.2010, 02:35
И поводу параметров функции SendPacket, нужно опять таки выделять дето память и передавать указатели на необходимые структуры?ей слава богу можно выделять память как угодно ибо освобождать память придётся так же твоей функции а не клиенту)
Помоему должна быть еще функция ниже уровнем, колторой можно передать обычный пакет а не строки с командами на которые нужно еще найти описание или долго изучать.ну она есть, только принимает пакеты в зашифрованном виде, но если её хукнуть посередине (точнее после того как она пакет дешифровала) и имитировать работу до середины и после (в зависимости от того посылаешь ты пакет или перехватываешь) то всё будет пучком)

Yegor
19.05.2010, 03:02
xkor, интересно зачем это ей передается пакет в зашифрованном виде? :) Она его что дешифрует и потом опять шифрует?

Отправка в сеть я вижу идет все таки в функции sendpacket
engine.dll:203B0CD8 call near ptr ws2_32_send

Prixmegently
19.05.2010, 14:36
Можно использовать локальные переменные, не нужно выделять память в ла2 для SendPacket.
Формат может быть любым, главное, чтобы пакет потом получился корректный. Пакет, естественно, не зашифрованный.

Format -
с = byte;
h = word;
d = dword;
Q = int64;
S = wchar*;
b = dword (size), array;

А вот примеры использования.

1F=Action:d(ObjectID)d(OriginX)d(OriginY)d(OriginZ )c(ActionID 0-Simple click, 1-Shift click)

SendPacket(SendPacketObj, "cddddc", 0x1F, ItemList[i].ObjectId, User.X, User.Y, User.Z, 0x0);

0F=MoveBackwardToLocation:d(ToX)d(ToY)d(ToZ)d(Orig inX)d(OriginY)d(OriginZ)d(MoveMovement)

SendPacket(SendPacketObj, "cddddddd", 0x0F, NpcList[i].X, NpcList[i].Y, NpcList[i].Z, User.X, User.Y, User.Z, 0x0);

Yegor
19.05.2010, 15:46
Prixmegently, SendPacketObj это тот же объект который я например передавал при вызове функции RequestFriendlist?

Prixmegently
19.05.2010, 16:53
Нет, там отличный объект от Request*, AddNetworkQueue. Я перехватываю SendPacket, чтобы узнать адрес объекта.

Yegor
19.05.2010, 17:23
Адрес объекта для всех типов пакетов одинаков и не меняется во время одной игровой сесии?

И я имел ввиду не Request, а объект UNetworkHandler
который приходит в пакете AddNetworkQueue. Я смотрел простую функцию создания простейшего пакета сотсоящего только из ID, так там вроде передается именно этот объект, хотя я могу ошибаться.

Prixmegently
19.05.2010, 17:32
Методы Request*** и AddNetworkQueue одного объекта UNetworkHandler. Создается он один раз при запуске клиента. SendPacket - там другой объект.

Yegor
19.05.2010, 17:52
Prixmegently, понял вери биг сенк, кто бы еще так разъяснил. Осталось только чтобы фроост был милостлив :)

xkor
20.05.2010, 10:58
SendPacket - там другой объект.оО, а у меня на всех клиентах что проверял бал тот же UNetworkHandler
xkor, интересно зачем это ей передается пакет в зашифрованном виде? Она его что дешифрует и потом опять шифрует?я думал ты про функцию в которую пакеты от сервера попадают, а на отправку да, SendPacket принимает формат пакета, собирает пакет и шифрует его, ток хз в каком месте она его шифрует...

Yegor
20.05.2010, 12:05
xkor, уже получилось, отправил пакет, всем спасибо

mira
28.07.2011, 11:50
оО, а у меня на всех клиентах что проверял бал тот же UNetworkHandler
я думал ты про функцию в которую пакеты от сервера попадают, а на отправку да, SendPacket принимает формат пакета, собирает пакет и шифрует его, ток хз в каком месте она его шифрует...

в аднетводке итд unetworkhandler, в сенде закрытый класс вроде unetwork являющийся членом unetworkhandler-а. Один из членов (толи методов) unetwork- указатель на процедуру шифрации и именно его подменят ряд защит. Функция канечноже накрыта wl-ом :)

Добавлено через 8 минут
В сенде этот участок выглядит примерно:

MOV EDX, [UNetworkHandler+хх]
....
Call [EDX]

Demion
28.07.2011, 17:50
SendPacket(*(VOID**)(UNetworkHandler+48), "cddddc", 0x1F, ObjectID, OrignX, OrignY, OrignZ, ActionID);

destructor
01.08.2011, 18:45
вопрос из лички, продублирую сюда

некомпилит это место:
DWORD addr=0;
int __stdcall jmpMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption, UINT uType)
{
__asm jmp [addr];
};

эти кракозяблы дописал форум, на самом деле там квадратные скобки
__asm jmp [addr];

user713
25.03.2014, 14:28
Всем доброго времени суток! Помогите нубу реализовать такую штуку:
При старте клиента появляется картинка, как сделать, чтобы она не отображалась? Хукнул "CreateNSplashScreenClassInstance" из nsplash.dll, меседж бокс выводится из хукнутой функции, но после его закрытия закрывается и клиент. Прошу сильно не пинать, с хуками впервые общаюсь! Если капаю не в том направлении, направьте на путь истинный! Пробовал возвращать клиенту и инты (0, 1, -1) и булины, результат один и тотже.
Уже неделю читаю ваш форум - классно, интересно, но пока не всё еще понятно :)

SeregaZ
25.03.2014, 14:59
ладва? не проше ли в л2.ини отключить отображение картинки? помница там что-то было такое...

user713
25.03.2014, 15:09
Проще. Но я с хуками пытаюсь разобраться. Интересно не через ини сделать, а через *опу :)

cvillian
28.03.2015, 15:52
Методы Request*** и AddNetworkQueue одного объекта UNetworkHandler. Создается он один раз при запуске клиента. SendPacket - там другой объект.

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

Breadfan
29.03.2015, 10:16
Ну раз у тебя хук ставится - то хендл ты получишь при первом же его срабатывании - из ecx.

thiscall-вызов: https://msdn.microsoft.com/ru-ru/library/ek8tkfbw.aspx

cvillian
29.03.2015, 11:23
Ну раз у тебя хук ставится - то хендл ты получишь при первом же его срабатывании - из ecx.

thiscall-вызов: https://msdn.microsoft.com/ru-ru/library/ek8tkfbw.aspx

Да, но для этого нужно оформить код на asm, а я в нём не в зуб ногой)

Так это должно выглядеть?

var
uh: THandle;
begin
asm
mov uh,ecx // uh - моя переменная в которую я записываю значение из ecx.
end;

ScythLab
29.03.2015, 20:10
Примерно так, только при одном маленьком условии: если ты этот кусок кода поставишь в нужно место (и для сохранения лучше использовать глобальную переменную)

Breadfan
02.04.2015, 17:46
чуть дополню - "нужное место" - это такое место, которое обязательно будет вызвано ДО всех твоих манипуляций с ручным вызовом функций клиента, иначе есть вероятность попытки вызова с нулем (или мусором) в регистре, что к хорошему врятли приведет.

cvillian
08.04.2015, 03:36
Сорри за оф-топ, просто хотел сказать, что замечательно что тут кто-то ещё отзывается :) На алл-читс всё уже мертво к сожалению :(

user713
08.04.2015, 18:12
...При старте клиента появляется картинка, как сделать, чтобы она не отображалась?...

Сам себе и отвечу(через год xD):
typedef HANDLE (__stdcall *_CreateFile) (LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);
_CreateFile true_CreateFile;

HANDLE __stdcall new_CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
{
HANDLE fileHandle = 0;
wchar_t * pos = wcsstr((wchar_t *)lpFileName, L".bmp");
if(pos != NULL && ShowSplash)
fileHandle = true_CreateFile(L"null\\null", dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
else
fileHandle = true_CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
return fileHandle;
}

void hook()
{
FARPROC addr;
if ((addr = GetProcAddress(LoadLibraryA("kernel32.dll"), "CreateFileW")) == 0) ShowSplash = false;
true_CreateFile = (_CreateFile) splice((unsigned char*) addr, new_CreateFile);
}

Хукаем CreateFileW из библиотеки kernel32.dll, и запрещаем клиенту открывать файлы в имени которых присутствуют символы ".bmp".

Breadfan
08.04.2015, 18:41
Залез в папку systextures клиента - удалил *.bmp....Но - да, не сгодитса при запуске через апдейтеры...

cvillian
26.06.2015, 01:07
Нашёл тут на форуме пример на с++, но клиент вылетает с критом после месседж бокса, никак не получается заставить работать.

#include "stdafx.h"
#include <Windows.h>

#pragma pack(push, 1)
struct jmp_struct {
BYTE instruction;
DWORD argument;
};
#pragma pack(pop)

BYTE old[5];
DWORD written = 0;
jmp_struct jump;

typedef DWORD(__fastcall *t_Func)(void* param1)
t_Func Original_Func = NULL;

INT __fastcall Intercept_Func(void* param1)

{
// Сохранение контекста класса перехваченного метода.
DWORD this_ptr = NULL;
__asm mov this_ptr, ecx;

// Восстанавливаем 5 первых байт функции.
WriteProcessMemory(GetCurrentProcess(), (void*)Original_Func, (void*)&old, 5, &written);

MessageBox(0, L"Hooked", L"", MB_OK);

// Восстановление в регистр ecx контекста класса перехваченного метода.
__asm mov ecx, this_ptr;
DWORD originalResult = Original_Func(param1/*, param2*/);

// Снова заменяем 5 байт функции на команду перехода на нашу функцию
WriteProcessMemory(GetCurrentProcess(), (void*)Original_Func, (void*)&jump, 5, &written);

return originalResult;
}

void InterceptFunctions() {
HMODULE hEngine = GetModuleHandleA("engine.dll");

Original_Func = (t_Func)GetProcAddress(hEngine, "?RequestBypassToServer@UNetworkHandler@@UAEHAAVL2P aramStack@@@Z");

if (Original_Func == 0) {
MessageBox(NULL, L"Can't get address", L"Error!", MB_OK);
return;
}

jump.instruction = 0xE9;
jump.argument = (DWORD)&Intercept_Func - ((DWORD)Original_Func + 5); // Смещение для jmp = <адрес назначения> - (<адрес jmp> + sizeof(BYTE) + sizeof(int))

ReadProcessMemory(GetCurrentProcess(), (void*)Original_Func, (void*)&old, 5, &written);
WriteProcessMemory(GetCurrentProcess(), (void*)Original_Func, (void*)&jump, sizeof(jmp_struct), &written);
}


BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: InterceptFunctions();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

Yegor
26.06.2015, 11:29
Какие хроники? Что написано в MessageBox?

cvillian
27.06.2015, 08:31
Хроники Эпилог, меседж бокс я уже убрал - раз попадает в мою функцию, но всё равно крит.

Update

если я в описании типа ставлю __stdcall - крита нет, но диалоги перестают работать, т.е. как я понял параметр не передаётся в оригинальную функцию

typedef DWORD(__stdcall *t_Func)(void* param1/*, int param2*/);

ScythLab
28.06.2015, 23:40
Попробуй
typedef DWORD(__fastcall *t_Func)(DWORD this_ptr, int dummy, void* param1)
В dummy можно передавать, что угодно.
Свою Intercept_Func лучше также объявить, а то эти ассемблерные вставки с ecx какая-то порнография.

cvillian
22.07.2015, 20:13
А как работает ИГ волкер? Т.е. что нужно сделать, чтобы открыть свою форму в кленте? И можно ли это сделать на дельфи?

ScythLab
23.07.2015, 20:47
В Engine.dll есть 2 класса: UCanvas и FCanvasUtil, с помощью них можно рисовать внутри клиента, возможно люди обходятся этими двумя классами, а может есть еще какой-нить класс для создания полноценного окна.

cvillian
23.07.2015, 22:20
Я думал что-то вроде инжекта своей длл с формой

xixi
24.07.2015, 01:59
Ну почему же....можно.
Вот у меня при инжекте dll почему-то не происходит ничего. И я не знаю с чем это связано - никаких ошибок.

cvillian
24.07.2015, 14:47
Ну почему же....можно.
Вот у меня при инжекте dll почему-то не происходит ничего. И я не знаю с чем это связано - никаких ошибок.
Это ты через библиотеку advApiHook.pas делаешь?

ScythLab
24.07.2015, 22:43
Я думал что-то вроде инжекта своей длл с формойВон ты о чем, я думал ты хочешь внутри ла2 рисовать формы (с помощью окон-ла2).
Можно и свою форму в dll запихнуть, только возможно с VCL формами придется несколько пошаманить, а если уходить в сторону winApi и диалогов, то вообще никаких проблем.

cvillian
30.07.2015, 20:12
А возможно перехватывать пакеты не перхватывая функции из engine.dll, а
перехватывая например send и recv? или я тогда смогу только прослушивать а отправлять не смогу?

ScythLab
31.07.2015, 13:00
А возможно перехватывать пакеты не перхватывая функции из engine.dll, а
перехватывая например send и recv? или я тогда смогу только прослушивать а отправлять не смогу?если красиво заморочиться, то сможешь и прослушивать, и отправлять, и блокировать прием/отправку некоторых пакетов... только придется решать проблему с шифрацией трафика, а это значительно сложнее, чем написание обычного бота

cvillian
31.07.2015, 19:06
если красиво заморочиться, то сможешь и прослушивать, и отправлять, и блокировать прием/отправку некоторых пакетов... только придется решать проблему с шифрацией трафика, а это значительно сложнее, чем написание обычного бота

Ну коннект к логин серверу я взял тут готовый и сделал конект к гейм серверу. Наваял формочку с инвентарём, всё показывает, пакеты могу отправлять. Т.е. можно сказать что с шифацией разобрался. Но делать полноценного ООГ бота мне не под силу, да и не зачем - есть волкер.
Я думал о чём-то вроде небольшого плагина для клиента. Перехватывать функции из engine.dll - у меня тоже врятли получится, вот подумал на счёт Send и Recv

cvillian
02.08.2015, 22:09
Есть совет куда копать?

ScythLab
03.08.2015, 00:10
Есть совет куда копать?Если можешь расшифровать трафик, то влазишь внутрь клиента (любой удобный вид инъекции), и дальше сплайсишь send и recv, причем лучше в библиотеке ws2_32.dll.
C send'ом проще: делаешь сплайс, в своем обработчике получаешь буфер, делаешь с ним все что хочешь и передаешь управление оригинальной функции; с recv чуть сложнее: нужно подменять точку возврата, передавать управление оригинальной функции, дожидаешься окончание ее работы, проверяешь результат (если есть данные, обрабатываешь их), и возвращаешь управление в вызываемую функцию.
Вначале попробуй сделать перехват функций даже без действий, чтобы проверить контролирует ли клиент эти функции или нет, если не контролирует, то развиваешь функционал.
Есть еще прикольный вариант: пишешь свою библиотеку wsock32.dll, по набору экспортируемых функций идентичную оригинальной, которая только передает управление библиотеке из system32, ну и плюс обрабатывает трафик, кое-где такой элементарный трюк работает.

cvillian
03.08.2015, 15:02
Функции перехватил:
function new_recv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
sz: Word;
id: byte;
begin
Form1.ListBox1.Items.Add('recv');
result:= old_recv(s, buf, len, flags);
end;

но непонятно как из Buf выковыривать информацию

ScythLab
03.08.2015, 15:44
Функции перехватил:
function new_recv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
sz: Word;
id: byte;
begin
Form1.ListBox1.Items.Add('recv');
result:= old_recv(s, buf, len, flags);
end;

но непонятно как из Buf выковыривать информациюОбъяви его как PAnsiChar (либо ссылка на статический массив байт), возможно тот же эффект даст обычное преобразование PAnsiChar(Buff).

cvillian
06.08.2015, 01:52
с recv чуть сложнее: нужно подменять точку возврата, передавать управление оригинальной функции, дожидаешься окончание ее работы, проверяешь результат (если есть данные, обрабатываешь их), и возвращаешь управление в вызываемую функцию.

С этим я походу уже не справлюсь.

ScythLab
06.08.2015, 17:37
С этим я походу уже не справлюсь.Не бойся, это не так страшно как кажется, к тому же все это описано с учетом различных защит и их проверок, т.е. чтобы быть максимально незаметным, начинать можно с простых конструкций (как у тебя описан new_recv).

cvillian
08.08.2015, 17:35
т.е. чтобы быть максимально незаметным

Да незаметность мне не важна, я играю на бото-сервере :)
Просто я не знаю как делаются такие вещи как подмена точки возврата

ScythLab
08.08.2015, 23:22
Да незаметность мне не важна, я играю на бото-сервере :)
Просто я не знаю как делаются такие вещи как подмена точки возвратаЕсли неважна, тогда забудь об этом :)
Берешь вначале вызываешь оригинальную функцию recv, если она вернула данные, то обрабатываешь их, потом возвращаешь результат вызываемой программе.
Также не забывай учитывать flags = MSG_PEEK, а то будут косяки.

cvillian
08.12.2015, 16:56
А на c# нет случайно ни у кого примера конекта к серверу, без декодирования, просто чтобы первый пакет хотябы принял. В дельфи там сокет цеплялся на хэндл формы, посылал ей сообщения и было просто, с с# как-то по другому организуется, никак не могу разобраться

Добавлено через 3 часа 19 минут
а вообще вроде получилось :)
Только вот врятли переведу шифрацию с дельфи с#

mikser
03.06.2016, 09:27
Хуки путём патчинга кода функций и другие техники популярно (прям как для дебилов описывает даже я понял) описаны в книге по руткитам Хоглунда. Перевёл её в PDF фомат и нашёл английскую пдфку.
[Greg_Hoglund,_Jamie_Butler]_Professional_-_Rootki.pdf - английская пдфка
[Greg_Hoglund,_Jamie_Butler]_Rootkits_Subverting.chm - английская чхм-лька
Руткиты. Внедрение в ядро Windows - Hoglund - 2007.djvu - русская дижавю с оглавлением
Руткиты. Внедрение в ядро Windows - Hoglund - 2007.pdf - русская отклеарсканеная (ClearScan) пдфка с оглавлением
Всё в одном архиве
хттпс://yadi.sk/d/dS-YVeYBs9oYL
Source Code к книге пока не нашёл

respown
09.07.2016, 10:29
Yegor, тогда AddNetworkQueue
Есть такой вопросец. В общем попытался я перехватить эту функцию с помощью удаленных потоков и перезаписью таблицы импорта на asterios.tm, но обнаружил для себя - что такого метода в их версии Engine.dll - попросту нет. Либо они его переписали под себя(изменили название методов) либо еще что. Не суть важно. Попытался - посмотреть под отладчиком что там вообще происходит, l2 при виде отладчика сразу критует, судя по всему так задуманно, так как они используют у себя АПИ функцию IsDebuggerPresent, и в случае если она вернет истину - просто критуют клиента. Решил значит я с помощью перетирания таблицы импорта просто заменить эту функцию своей. Но пока что безуспешно, либо я где то ошибся, либо они вызывают ее по хитрому, и нужно копать глубже. Так вот возникает вопрос - может уже кто то колупался на клиенте астериоса с этим ? Знает кто то, на какие функции они поменяли исходные?

f1redark
16.07.2016, 21:49
Есть такой вопросец. В общем попытался я перехватить эту функцию с помощью удаленных потоков и перезаписью таблицы импорта на asterios.tm, но обнаружил для себя - что такого метода в их версии Engine.dll - попросту нет. Либо они его переписали под себя(изменили название методов) либо еще что. Не суть важно. Попытался - посмотреть под отладчиком что там вообще происходит, l2 при виде отладчика сразу критует, судя по всему так задуманно, так как они используют у себя АПИ функцию IsDebuggerPresent, и в случае если она вернет истину - просто критуют клиента. Решил значит я с помощью перетирания таблицы импорта просто заменить эту функцию своей. Но пока что безуспешно, либо я где то ошибся, либо они вызывают ее по хитрому, и нужно копать глубже. Так вот возникает вопрос - может уже кто то колупался на клиенте астериоса с этим ? Знает кто то, на какие функции они поменяли исходные?

Есть там все, без этого метода игра не будет работать, и он есть в таблице экспорта engine.dll, потому что l2.exe его импортирует, нельзя его выкинуть.

respown
18.07.2016, 00:41
Есть там все, без этого метода игра не будет работать, и он есть в таблице экспорта engine.dll, потому что l2.exe его импортирует, нельзя его выкинуть.
dumpbin говорит об обратном + ко всему GetProcAdress возвращает нулевой указатель. В игре есть папка system и asterios, в папке system - лежит Engine.dll и в ней такой метод есть, но эту библиотеку игра не грузит. Они грузят engine.dll из папки asterios, а там такого метода нет. Более того, там в одном месте только упоминается название класса, в котором этот метод реализован(UNetworkHandler) или как то так, даже конструкторы данного класса отсуствуют в таблице импорта. Хотя в оригинальной версии библиотеки - они есть.

xixi
18.07.2016, 02:30
Астериос не так прост как кажется. Нужно скрывать себя, чтобы нормально отлаживать...

respown
18.07.2016, 03:25
Астериос не так прост как кажется. Нужно скрывать себя, чтобы нормально отлаживать...
Я уже пробовал качать олю, кучу плагинов на скрытие, ничего не помогло, хотя через API Monitor можно хукать АПИ вызовы. Отладчиком пока не получилось подцепится.

ScythLab
19.07.2016, 23:27
они используют у себя АПИ функцию IsDebuggerPresentЭту функцию никто всерьез не использует, существует множество других способов обнаружения отладчика. Я в этой теме не силен, но могу посоветовать использовать для Ольки соответствующие плагины (типа Phantom), так же можно переименовать Ольку во что-нибудь другое (поищи в инете как это правильно делать), ну и на XP эти плагины работают лучше за счет драйверов плагинов. Также в ряде случаев лучше работает подключение к уже работающему клиенту, чем попытка запускать клиент непосредственно из Ольки.

dumpbin говорит об обратном + ко всему GetProcAdress возвращает нулевой указательОни используют библиотеки от Ertheia, после какого-то обновления данных хроник большинство вкусных классов и методов были убраны из экспорта, и поэтому их через GetProcAddress не найдешь, хотя они всё равно все остались внутри библиотеки.

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

Yegor
20.07.2016, 17:35
Они используют библиотеки от Ertheia, после какого-то обновления данных хроник большинство вкусных классов и методов были убраны из экспорта, и поэтому их через GetProcAddress не найдешь, хотя они всё равно все остались внутри библиотеки.


есть мысли как найти адреса этих функций? Нужно не для Астериоса а вообще для реализации бота на АПИ на последних L2 хрониках.

ScythLab
20.07.2016, 21:37
есть мысли как найти адреса этих функций? Нужно не для Астериоса а вообще для реализации бота на АПИ на последних L2 хрониках.Общая мысль очень простая: ищешь в библиотеке ссылки на созданные объекты UNetworkHandler и UGameEngine, а от них уже достаточно легко найдешь все интересующие методы. Все изыскания лучше начинать с предыдущих хроник и дальше данный алгоритм перенести на свеженький клиент.
Реализацию озвучивать не буду: кто реально захочет (и ранее разбирался с HF и подобными хрониками) за денек найдет решение.

Yegor
21.07.2016, 00:42
ищешь в библиотеке ссылки на созданные объекты UNetworkHandler и UGameEngine
наверно имеется ввиду в памяти процесса, откуда в библиотеке быть объектам.

Честно говоря как найти эти созданные объекты иначе как перехватив их при вызове их методов не придумал :(.

ScythLab
21.07.2016, 16:43
наверно имеется ввиду в памяти процесса, откуда в библиотеке быть объектам.Сам объект конечно будет в памяти процесса, но я писал про ссылки.

Yegor
21.07.2016, 17:32
Что за ссылки?

Smwr
22.07.2016, 12:51
Наверное, он имел ввиду, что указатель на экземпляр лежит по адресу 0x20000000 (или куда там грузится engine.dll) + фиксированное смещение.
А если ты собрался делать бота ничего не хукая, скажи, если не секрет, как баффы/скилллист/инвентарь получать собрался ? :)

ScythLab
26.07.2016, 13:16
Smwr, я сомневаюсь, что ты найдешь эту информацию в открытом доступе, базовое описание (и возможно единственное, которое я встречал) это Радар своими руками (http://coderx.ru/showthread.php?t=1077). Если и есть какая-то доп. информация, то возможно только в каких-нибудь подпольях или непосредственно от разрабов утилит для ла2.
Если бы я шел по этому пути, то поступил бы так:
- посмотрел бы список экспортируемых функций;
- нашел бы там наиболее интересные (типа GetNextNPC, GetNextParty и подобных);
- в IDA и под отладчиком посмотрел бы как они работают (по сути нужно узнать откуда они берут данные);
- можно по найденным названиям попробовать поискать инфу, вдруг где-то что-то проскользнет;
- дальше либо использовать эти функции, либо разобраться во внутренних структурах ла2 и читать данные напрямую.

Работа очень и очень геморройная и объемная.
К тому же есть ньюанс: если ты не перехватываешь события (OnXxx), то тебе придется периодически шерстить все списки, чтобы у тебя была актуальная информация.
Но зато есть существенный плюс: гварду определить такого бота будет нереально трудно, т.к. целостность данных ла2 никаких образом не нарушается.

Smwr
27.07.2016, 16:40
ScythLab, я выяснил, что баффлистов целых 2 штуки, один лежит по статическому адресу(как мне показалось), но там нет времени окончания. А вот второй содержит все что мне нужно, но как его брать в рантайме не пойму. У меня есть подозрение, что его все таки содержит структура User, потому что он переразмещается при ее обновлении, но то ли там глубоко вложенный указатель, то ли слишком узкий диапазон выбрал для поиска..
Под отладчиком запускать пробовал один раз(под идой), клиент кританул, что там обысно юзают, олю с плагинами?
Еще бы под msvs запустить л2, а то отлаживать с логированием в файл гемор.
Что касается обновления через регулярное перечитывание - само собой разумеется, только хз, по скорости наверное даже хуже классического перехвата пакетов выйдет...
А что касается защиты - пишу для себя просто ради интереса, использовать на сервере, где защиты нет впринципе.

ScythLab
28.07.2016, 01:17
Smwr, из отладчиков Олька + плагины.
Для анализа очень помогает IDA (только нужен либо engine незашифрованный, либо самому расшифровать).
В либе например есть интересные методы User::AddBuff/HaveBuff/ClearBuff, в них видно, что в User хранится список (FArray) бафов (MagicSkillUniqueKey).
Я думаю там много всего интересного, главное не устать искать.
А по поводу плавающих адресов: каждая информация всегда от чего-то отталкивается, так что если ты сможешь найти всю цепочку, тогда у тебя будет 100% результат, по крайней мере внутри одних хроник.

Smwr
28.07.2016, 02:08
ScythLab, оО, у меня такого в списке экспорта нет. буду смотреть распакованную.

xixi
01.08.2016, 00:33
Нафиг смотреть список экспорта? В иде находите функцию и считайте оффсет.

Smwr
01.08.2016, 11:11
/del

ScythLab
11.08.2016, 22:26
ScythLab, оО, у меня такого в списке экспорта нет. буду смотреть распакованную.лучше начинать играться на HF/GoD, тогда всё будет, а дальше уже переходить на последние хроники

Нафиг смотреть список экспорта? В иде находите функцию и считайте оффсет.чтобы найти эти функции, нужно вначале понять как они "выглядят", а для этого на первых порах нужны экспортные функции

Smwr
13.08.2016, 23:37
лучше начинать играться на HF/GoD, тогда всё будет, а дальше уже переходить на последние хроники
так я и играюсь с хф(рпг) и такого нет. сегодня наконец окажусь дома, гляну что там в анпакеутой длл.

Smwr
20.08.2016, 05:51
В либе например есть интересные методы User::AddBuff/HaveBuff/ClearBuff, в них видно, что в User хранится список (FArray) бафов (MagicSkillUniqueKey).
учитывая, что в хф этого нет, а в годе есть - скорее всего это не то что я ищу.

и вот что еще не понятно - в хф таблица методов user не экспортируется, как её можно достать?

ScythLab
22.08.2016, 10:43
учитывая, что в хф этого нет, а в годе есть - скорее всего это не то что я ищу.Да ты прав, эти методы появились только в GoD, значит нужно искать в другом месте, попробуй покопай пакет MagicEffectIcons (0х85), там где-то должен быть обработчик этого пакета, если найдешь его, то скорей всего найдешь где хранятся бафы.

и вот что еще не понятно - в хф таблица методов user не экспортируется, как её можно достать?Для начала нужно найти созданный объект User, дальше просматриваешь его VMT (Virtual method table), и в Ida изучаешь, что делает каждый метод :-)

Smwr
22.08.2016, 15:16
Чем дальше в лес..... :scratch_one-s_head:


Для начала нужно найти созданный объект User, дальше просматриваешь его VMT (Virtual method table), и в Ida изучаешь, что делает каждый метод :-)
VMT первый член? У объекта User первый член - 0...

попробуй покопай пакет MagicEffectIcons (0х85), там где-то должен быть обработчик этого пакета, если найдешь его, то скорей всего найдешь где хранятся бафы.
За наводку спасибо, как то пропустил этот пакет, но... где таблица обработчиков, какой объект обрабатывает? UNH?
Там же, если я всё правильно понимаю, порядок вызовов такой: DispatchNetworkQueue->ТотСамыйОбработчик->UGameEngine::OnXXX?
Взял для примера OnUserInfo, то ли Идой пользоваться не умею, то ли в упор не вижу, откуда она вызывается.

Всё таки, вроде нашёл обработчик. Имя правда немного другое.
.text:203EF5B0 sub_203EF5B0 proc near ; DATA XREF: sub_20479CD0+CC3o
.text:203EF5B0
.text:203EF5B0 var_44 = byte ptr -44h
.text:203EF5B0 var_24 = dword ptr -24h
.text:203EF5B0 var_20 = dword ptr -20h
.text:203EF5B0 var_1C = dword ptr -1Ch
.text:203EF5B0 var_18 = dword ptr -18h
.text:203EF5B0 var_14 = dword ptr -14h
.text:203EF5B0 var_10 = dword ptr -10h
.text:203EF5B0 var_C = dword ptr -0Ch
.text:203EF5B0 var_4 = dword ptr -4
.text:203EF5B0 arg_0 = dword ptr 8
.text:203EF5B0 arg_4 = dword ptr 0Ch
.text:203EF5B0
.text:203EF5B0 push ebp
.text:203EF5B1 mov ebp, esp
.text:203EF5B3 push 0FFFFFFFFh
.text:203EF5B5 push offset SEH_203EF5B0
.text:203EF5BA mov eax, large fs:0
.text:203EF5C0 push eax
.text:203EF5C1 mov large fs:0, esp
.text:203EF5C8 sub esp, 38h
.text:203EF5CB push ebx
.text:203EF5CC push esi
.text:203EF5CD push edi
.text:203EF5CE mov [ebp+var_10], esp
.text:203EF5D1 xor ebx, ebx
.text:203EF5D3 mov [ebp+var_4], ebx
.text:203EF5D6 mov [ebp+var_14], ebx
.text:203EF5D9 mov ecx, ?GL2Console@@3PAVUL2ConsoleWnd@@A ; UL2ConsoleWnd * GL2Console
.text:203EF5DF mov eax, [ecx]
.text:203EF5E1 mov edx, [eax+31Ch]
.text:203EF5E7 call edx
.text:203EF5E9 lea eax, [ebp+var_14]
.text:203EF5EC push eax
.text:203EF5ED push offset asc_205098C8 ; "h"
.text:203EF5F2 mov ecx, [ebp+arg_0]
.text:203EF5F5 mov edx, [ecx+48h]
.text:203EF5F8 push edx
.text:203EF5F9 mov eax, [ebp+arg_4]
.text:203EF5FC call sub_203D9BA0
.text:203EF601 add esp, 0Ch
.text:203EF604 mov edi, eax
.text:203EF606 movsx eax, word ptr [ebp+var_14]
.text:203EF60A lea eax, [eax+eax*2+1]
.text:203EF60E push eax
.text:203EF60F lea ecx, [ebp+var_44]
.text:203EF612 call ??0L2ParamStack@@QAE@H@Z ; L2ParamStack::L2ParamStack(int)
.text:203EF618 mov byte ptr [ebp+var_4], 1
.text:203EF61C movsx eax, word ptr [ebp+var_14]
.text:203EF620 cdq
.text:203EF621 push edx
.text:203EF622 push eax
.text:203EF623 lea ecx, [ebp+var_44]
.text:203EF626 mov esi, ?PushBack@L2ParamStack@@QAEH_J@Z ; L2ParamStack::PushBack(__int64)
.text:203EF62C call esi ; L2ParamStack::PushBack(__int64)
.text:203EF62E mov [ebp+var_18], ebx
.text:203EF631
.text:203EF631 loc_203EF631: ; CODE XREF: sub_203EF5B0+DDj
.text:203EF631 movsx ecx, word ptr [ebp+var_14]
.text:203EF635 cmp [ebp+var_18], ecx
.text:203EF638 jge short loc_203EF68F
.text:203EF63A mov [ebp+var_1C], ebx
.text:203EF63D mov [ebp+var_20], ebx
.text:203EF640 mov [ebp+var_24], ebx
.text:203EF643 lea edx, [ebp+var_24]
.text:203EF646 push edx
.text:203EF647 lea eax, [ebp+var_20]
.text:203EF64A push eax
.text:203EF64B lea ecx, [ebp+var_1C]
.text:203EF64E push ecx
.text:203EF64F push offset aDhd ; "dhd"
.text:203EF654 mov edx, [ebp+arg_0]
.text:203EF657 mov eax, [edx+48h]
.text:203EF65A push eax
.text:203EF65B mov eax, edi
.text:203EF65D call sub_203D9BA0
.text:203EF662 add esp, 14h
.text:203EF665 mov edi, eax
.text:203EF667 mov eax, [ebp+var_1C]
.text:203EF66A cdq
.text:203EF66B push edx
.text:203EF66C push eax
.text:203EF66D lea ecx, [ebp+var_44]
.text:203EF670 call esi ; L2ParamStack::PushBack(__int64)
.text:203EF672 movsx eax, word ptr [ebp+var_20]
.text:203EF676 cdq
.text:203EF677 push edx
.text:203EF678 push eax
.text:203EF679 lea ecx, [ebp+var_44]
.text:203EF67C call esi ; L2ParamStack::PushBack(__int64)
.text:203EF67E mov eax, [ebp+var_24]
.text:203EF681 cdq
.text:203EF682 push edx
.text:203EF683 push eax
.text:203EF684 lea ecx, [ebp+var_44]
.text:203EF687 call esi ; L2ParamStack::PushBack(__int64)
.text:203EF689 add [ebp+var_18], 1
.text:203EF68D jmp short loc_203EF631
.text:203EF68F ; ---------------------------------------------------------------------------
.text:203EF68F
.text:203EF68F loc_203EF68F: ; CODE XREF: sub_203EF5B0+88j
.text:203EF68F mov ecx, ?GL2Console@@3PAVUL2ConsoleWnd@@A ; UL2ConsoleWnd * GL2Console
.text:203EF695 mov edx, [ecx]
.text:203EF697 lea eax, [ebp+var_44]
.text:203EF69A push eax
.text:203EF69B mov edx, [edx+320h]
.text:203EF6A1 call edx
.text:203EF6A3 movsx eax, word ptr [ebp+var_14]
.text:203EF6A7 push eax
.text:203EF6A8 push offset aReceiveAbnorma ; "(Receive)AbnormalStatusUpdatePacket : %"...
.text:203EF6AD mov ecx, ?GNetworkLog@@3PAVFOutputDevice@@A ; FOutputDevice * GNetworkLog
.text:203EF6B3 mov edx, [ecx]
.text:203EF6B5 push edx
.text:203EF6B6 call ?Logf@FOutputDevice@@QAAXPB_WZZ ; FOutputDevice::Logf(wchar_t const *,...)
.text:203EF6BC add esp, 0Ch
.text:203EF6BF mov byte ptr [ebp+var_4], bl
.text:203EF6C2 lea ecx, [ebp+var_44]
.text:203EF6C5 call ??1L2ParamStack@@QAE@XZ ; L2ParamStack::~L2ParamStack(void)
.text:203EF6CB xor al, al
.text:203EF6CD mov ecx, [ebp+var_C]
.text:203EF6D0 mov large fs:0, ecx
.text:203EF6D7 pop edi
.text:203EF6D8 pop esi
.text:203EF6D9 pop ebx
.text:203EF6DA mov esp, ebp
.text:203EF6DC pop ebp
.text:203EF6DD retn
.text:203EF6DD sub_203EF5B0 endp


Что в нём происходит непонятно, особенно
.text:203EF68F mov ecx, ?GL2Console@@3PAVUL2ConsoleWnd@@A ; UL2ConsoleWnd * GL2Console
.text:203EF695 mov edx, [ecx]
.text:203EF697 lea eax, [ebp+var_44]
.text:203EF69A push eax
.text:203EF69B mov edx, [edx+320h]
.text:203EF6A1 call edx

По идее, где то внутри, должен быть вызов APawn::UpdateAbnormalState(APawn *this, float), потому что ничего похожего со словом Abnormal больше нету, но либо чего то неэкспортируемого или вообще хз, этот call edx меня вводит в ступор.

Хотя, внутри UpdateAbnormalState тоже ничего хорошего - там вроде как оперируются видимые эффекты, так что это либо всё таки обработчик не тот (ну есть однозначный ShortBuffStatusUpdate, но хз используется ли он или, и там тоже call edx ведущий не понятно куда), либо хз.

ScythLab
22.08.2016, 17:10
DispatchNetworkQueue просматривает список доступных пакетов, дальше по Id пакета из специального массива со ссылками на обработчики (UGameEngine::OnXXX) вызывает нужный метод. У меня такое ощущение, что обработкой 0х85 пакета занимается не экспортируемый обработчик.


.text:203EF69B mov edx, [edx+320h]
.text:203EF6A1 call edxОбычно таким образом вызываются функции из VMT, т.е. в Ida ты нигде не увидишь прямой вызов на интересующую тебя экспортируемую функцию.

Smwr
22.08.2016, 18:06
DispatchNetworkQueue просматривает список доступных пакетов, дальше по Id пакета из специального массива со ссылками на обработчики (UGameEngine::OnXXX) вызывает нужный метод. У меня такое ощущение, что обработкой 0х85 пакета занимается не экспортируемый обработчик.
Перед Onxxx пакет должен быть дизасемблированн, ведь в Onxxx параметры передаются уже в виде сформированного ParamStack и т.п.
По идее, там ведь где то должен быть какой нибудь switch-case, в котором по id вызывается соответствующая функция-дизасемблер? Как бы найти, где происходит обработка id?
Других вариантов попасть в нее не вижу, тк таблицу методов обработчиков UGameEngine и методов предварительно разбирающих пакет я и подавно без понятия где брать.

Запустил таки клиент под ольгой на w7, пока немного осваиваюсь, позже попробую повешать брейкпоинтов.

ScythLab
22.08.2016, 19:01
UNetworkHandler::Tick в цикле вызывает DispatchNetworkQueue, полученный пакет с помощью FL2ReplayManager::AddPacketData преобразовывается в L2ParamStack, потом идет вызов OnXxx.

PS. Это справедливо для стандартного клиента, если имеется защита, то может быть дополнительный функционал по шифрованию/подмене данных, вплоть до того, что AddNetworkQueue и DispatchNetworkQueue вообще не используются.

Yegor
22.08.2016, 22:09
Запустил таки клиент под ольгой на w7


Поделитесь пожалуйста рецептом.

Smwr
23.08.2016, 12:20
Поделитесь пожалуйста рецептом.

сборка odbg110 9in1 for Themida + phantom (http://prntscr.com/c9992s).
я не аттачусь, а запускаю л2 из ольги(додумался так сделать только когда дошёл до этой сборки, поэтому она может быть и не принципиальна и сойдет обычная).

Smwr
24.08.2016, 16:49
Пошерстил функции обработки PartySpelled и MagicEffectIcons - внутри них не вызывается метод UGameEngine::On..., только из NWindow и пара общих для всех обработчиков из Engine.
И теперь хз, что делать - то ли методы всё таки не те, то ли еще чего.
Походу вся эта затея с отказом от хуков провальная, потому что вариантов кроме как хукать их и извлекать инфу оттуда я не вижу.

Yegor
24.08.2016, 23:28
сборка odbg110 9in1 for Themida + phantom (http://prntscr.com/c9992s).
я не аттачусь, а запускаю л2 из ольги(додумался так сделать только когда дошёл до этой сборки, поэтому она может быть и не принципиальна и сойдет обычная).

Не могу найти где скачать этот пак.
Скачал сборник odbg разных версий с разными утилитами но там черт ногу сломит.

Smwr
25.08.2016, 11:29
Не могу найти где скачать этот пак.
Скачал сборник odbg разных версий с разными утилитами но там черт ногу сломит.

https://tuts4you.com/download.php?view.2012
первая же ссылка в гугле

Yegor
25.08.2016, 19:54
По этой ссылке уже качал. Там битый архив.

NLObP
25.08.2016, 22:53
По этой ссылке уже качал. Там битый архив.

Там запароленный архив. Пароль: tuts4you

Yegor
26.08.2016, 01:31
Спасибо. Нужна была более свежая версия WinRar.

Добавлено через 1 час 30 минут
Ура!! Я отлаживаю процесс л2 :)

Smwr
26.08.2016, 09:05
/del

Yegor
26.08.2016, 12:57
Как в OllyDBG найти какой экспортируемой функции принадлежит просматриваемый код?

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

Smwr
26.08.2016, 20:44
Как в OllyDBG найти какой экспортируемой функции принадлежит просматриваемый код?

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

врядли такое есть.










А как узнать версию протокола клиента?

ScythLab
27.08.2016, 11:11
А как узнать версию протокола клиента?
Версия протокола используется только на пакетном уровне и только для OOG ботов (пакет клиент->сервер 0x0E=ProtocolVersion), зачем оно тебе?

PS. Тут недавно проскакивал вопрос про Asterios - действительно прикольный сервак, либо я безумно туплю, либо ребята хорошо над защитой поработали. На диске валяется engine.dll от последних хроник (Одиссей), но потом в памяти все это дело каким-то образом подменяется на библиотеку от GoD. Ну и плюс подчищается возможность работать с библиотекой: как минимум удаляется таблица экспорта, может еще что-то делают. Веселая зараза.

Smwr
27.08.2016, 11:36
Версия протокола используется только на пакетном уровне и только для OOG ботов (пакет клиент->сервер 0x0E=ProtocolVersion), зачем оно тебе?

А как определить, с каким клиентом я работаю?

ScythLab
29.08.2016, 22:44
А как определить, с каким клиентом я работаю?100% гарантированного варианта не знаю, вполне возможно что в клиенте где-то эта инфа зашита (встречал упоминания, что можно клиент вызывать с параметром L2ProtocolVersion и типа в ответ получишь версию клиента/протокола - не знаю на сколько это правда).
Один из вариантов который мне советовали - создавать базу с engine.dll (отличать их друг от друга можно по размеру файла/кода).
Наш вариант определения озвучивать не буду по личным причинам.
Можешь для начала все клиенты считать HF5 (как наиболее распространенный клиент), а можешь отдавать настройку на откуп пользователю.

Smwr
30.08.2016, 07:11
Один из вариантов который мне советовали - создавать базу с engine.dll (отличать их друг от друга можно по размеру файла/кода).

понятно, так и думал.

можно клиент вызывать с параметром L2ProtocolVersion

можно, но пока так и не въехал, откуда он ее берет.

Yegor
31.08.2016, 12:35
У кого есть какие мысли как заинжектить свою dll на руофе с фростом.

При попытке выделения памяти в процессе l2.exe через VirtualAllocEx возвращает ошибку - отказано в доступе даже если указать параметр PAGE_READONLY.

ScythLab
31.08.2016, 13:43
У кого есть какие мысли как заинжектить свою dll на руофе с фростом.

При попытке выделения памяти в процессе l2.exe через VirtualAllocEx возвращает ошибку - отказано в доступе даже если указать параметр PAGE_READONLY.Драйвера защиты рулят :)
Пиши в личку - подскажу в какую сторону копать.

Smwr
26.09.2016, 15:35
Разобрал почти все, что хотел.
А вопрос с баффами и скилллистом так и остался нерешенным :DDD
Нигде, кроме как в UI их нет, походу.

Точнее баффы юзера нашёл в двух экземплярах(как и писал), первый можно получить путём хитрых смещений, правда endtime там на момент приема пакета. но это впринципе не такая большая проблема, т.к. проверять, изменились ли баффы(сравнивая это самое время с предыдущим) не проблема.

А вот с баффами сопартийцев всё очень плохо.

Они хранятся в виде 2-мерного массива в классе NCIconTabCtrl который в свою очередь лежит в классе UWindowHandle, который лежит в классе UUIScript и вроде как рядом лежит OID обьекта. Дальше стало лень искать, откуда ноги растут. Ситуацию осложняет то, что в NWindow не экспортируется ничего полезного, а тамошний код сложнее поддается анализу.

Скиллист тоже нашёл только ввиде массива айдишников.

В итоге сижу и думаю, а стоило ли вообще заморачиваться с такой реализацией, т.к. всё равно без хуков не обойтись...

Назрел вопрос - при юзе скилла клиент сам считает его откат? Потому что пакет C7=SkillCoolTime:d(listSize:Loop.01.0004)d(skillID :Get.Skill)d(skillLvl)d(reuseDelay)d(timeRemain) приходит только при открытии скилллиста, мб есть еще что то?

ScythLab
26.09.2016, 20:01
Smwr, на сколько помню, если у перса есть неактивные скиллы, то SkillCoolTime автоматически приходит при входе в игру; если этого пакета не пришло, то считаем, что любой скилл можно юзать.
И после каждого использования скилла приходит пакет 48=MagicSkillUse, где указано время каста и время отката.
Дальше клиент сам считает, но и сервер параллельно тоже считает и не позволит раньше времени использовать скилл.

Smwr
27.09.2016, 10:59
Smwr, на сколько помню, если у перса есть неактивные скиллы, то SkillCoolTime автоматически приходит при входе в игру; если этого пакета не пришло, то считаем, что любой скилл можно юзать.
И после каждого использования скилла приходит пакет 48=MagicSkillUse, где указано время каста и время отката.
Дальше клиент сам считает, но и сервер параллельно тоже считает и не позволит раньше времени использовать скилл.

Большое спасибо, почему то не додумался посмотреть, что в magicskilluse есть время отката :eek:
Поковырял обработчик, и нашёл то, что искал.
А не находил ранее, потому что ожидал увидеть целое, а оказалось, что время в л2 в float/double.

Smwr
17.11.2016, 17:14
/del

nasta456
30.01.2019, 20:52
спасибо за ответы)