Всем доброго времени суток! Ребята. Я изучаю только С++ и в Асм я не такой знаток... так что очень прошу вашей помощи. Работаю с онлайн ММОРПГ MuOnline ( http://www.muonline.co.kr/ ). Собственно дело вот в чем. Доступа к коду у меня нету, поетому хукаю длл и работаю напрямую. Есть ф-ция для перерасчета характеристик персонажа, определена она так : Код (Text): #define gObjCalCharacter ((void(*)(int))0x004C2650) Ну собственно вызов таков: gObjCalCharacter(ID игрока); То есть она принимает ID игрока и по этому ID делает перерасчет характеристик персонажа. Мне нужно было сделать некоторые корективы данной ф-ции и в конец данной ф-ции хукнул свою, чтобы всякий раз при вызове данной ф-ции исполнялся нужный мне код (как я уже говорил прямого доступа к коду нет, и сервер вызывает данную ф-цию не только когда я это хочу (привел пример выше) но и еще когда он это щитает нужным). Проблема заключается в том что мне надо узнать для какого ID игрока была вызвана данная ф-ция. С чем собственно справится не могу. И очень прошу вашей помощи. Прикрепил ф-цию.
Если вам известен адрес данной функции, вы знаете, что конвенция вызова cdecl, знаете список формальных параметров и вам нужно получить фактический аргумент, так почему вы не сделаете стандартный хук сплайсингом и не решите проблему? Прошу пощения, что отвечаю вопросом на вопрос и, нет, я не еврей.
Чесно говоря не знал про сплайсинг. Почитал... цитирую "Splicing в переводе с английского - склеивание, сращивание. Суть этого метода перехвата функций заключается в замене первых байт функции на переход в функцию-перехватчик, которая сделает некоторые действия и вернет управление в программу." То есть при вызове пойдет вызов главной ф-ции сразу передается управление нашей ф-ции, после её завершения управление передается главной. Но тут маленькая проблемка выходит. Мне надо всё с точностью до наоборот. Чтобы сначала отработала главная ф-ция, а уже следом за ней моя. Надо именно так и увы иначе просто нельзя(в плане том что мне иначе не подойдет). Вызов своей хукнули очень просто - подвинули в главной ф-ции RETN и дописали вызов ф-ции из дллки. Просто вся моя проблема в том что к OllyDbg отношусь на Вы, и каких то крупных познаний в С++\АСМ пока что не имею Появилась конечно идея... хукнуть сплайсингом,считать параметр, вернутся в главную ф-цию. Но тут уже будет проблема в том что главная потеряет входящий параметр. Очень надеюсь на Вашу помощь!
Killbrum Попробуйте превратить эту процедуру в функцию: начальный фрагмент переделываем вот так: MOV EAX,DWORD PTR SS:[EBP+8] PUSH EAX PUSH ESI PUSH EDI а в конце вместо будет 004C4DCB |. 5F POP EDI 004C4DCC |. 5E POP ESI 004C4DCD |. 5B POP EAX 004C4DCE |. 8BE5 MOV ESP,EBP 004C4DD0 |. 5D POP EBP 004C4DD1 \. C3 RETN и на выходе функции получите искомое значение аргумента. ЗЫ Это будет работать при условии, что регистр EBX может быть изменен после выхода, если нет, то посмотрите, может быть такой фокус пройдет с регистром ESI или EDI.
Если вы скопируете его, ничего она не потеряет. to crypto Есть некоторая ненулевая вероятность, что из-за несохраненного регистра ebx, esi или edi программа упадет.
Killbrum Что конкретно не вышло? Вы не пробовали проанализировать вызовы сабрутины на предмет использования регистров ebx, esi, edi?
Не вышло вот что... сделали как Вы говорили... извините если что, мой уровень С++ не на столько хорош, если я не ошибаюсь если в ф-ции я указываю входящий аргумент то оно считывает его из стека? Верно? Вообщем было там число -2 милиарда. Это явно не то. Значения должны быть от 6400 до 7400. Только в этом диапазоне значений идут ID игроков (так задумано разработчиками, не знаю зачем). Не знаю может быть я в чем то не прав, очень надеюсь что вы меня поправите, просто как уже не раз замечал мои знания С++ и принципа работы С++ (то есть как оно всё внутри работает) не такие уж и хорошие. Сделал что Вы говорили. В дллке написал так: Код (Text): extern "C" _declspec(dllexport) void CalChar(int aIndex) { } Но результат не тот. EBX, EDI свободны, то есть не используются. Но это именно в этой ф-ции. Программа писана на Visual C++ 6.0 поетому думаю нет смысла проверять внутренние call. По своей памяти когда работал с АСМ на MCS - 51 я мог записать в ячейку памяти. Если бы тут так можно было то я думаю всё намного бы упростилось. А что если сразу после Код (Text): MOV EAX,DWORD PTR SS:[EBP+8] влепить Код (Text): PUSH EAX Если меня не подводит память то он будет висеть в стеке пока его от туда не заберут, но по принципу FILO (First Input Last Output), если не ошибаюсь здесь так, то учитывая это это число будет висеть в стеке пока его от туда не заберут, а заберу я его перед концом процедуры вызовом своей ф-ции. Вызываем свою функцию вот так: Код (Text): 004C4DCB . 5F POP EDI 004C4DCC . 5E POP ESI 004C4DCD . 58 POP EAX 004C4DCE . 8BE5 MOV ESP,EBP 004C4DD0 . 5D POP EBP 004C4DD1 . 68 C08E5D00 PUSH GameServ.005D8EC0 ; /FileName = "Addon.dll" 004C4DD6 . FF15 F4BC8C0C CALL DWORD PTR DS:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA 004C4DDC . 09C0 OR EAX,EAX 004C4DDE . 0F84 B1610D00 JE GameServ.0059AF95 004C4DE4 . 68 F58E5D00 PUSH GameServ.005D8EF5 ; /ProcNameOrOrdinal = "CalChar" 004C4DE9 . 50 PUSH EAX ; |hModule 004C4DEA . FF15 F0BC8C0C CALL DWORD PTR DS:[<&KERNEL32.GetProcAddress>] ; \GetProcAddress 004C4DF0 . FFD0 CALL EAX 004C4DF2 . C3 RETN Это дописал в конце ф-ции. Извиняюсь если что то не так...
Killbrum Насчет влепить: именно это я и советовал сделать, уж не знаю, как Вы это реализовали. Объясняю еще раз. Начало сабрутины: Код (Text): 004C2650 >/> 55 PUSH EBP 004C2651 |. 8BEC MOV EBP,ESP 004C2653 |. 81EC 0C010000 SUB ESP,10C 004C2659 |. 53 PUSH EBX 004C265A |. 56 PUSH ESI 004C265B |. 57 PUSH EDI 004C265C |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 004C265F |. 69C0 68190000 IMUL EAX,EAX,1968 Переделываем его в hex-редакторе: Код (Text): 004C2650 >/> 55 PUSH EBP 004C2651 |. 8BEC MOV EBP,ESP 004C2653 |. 81EC 0C010000 SUB ESP,10C 004C2659 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] 004C265C |. 53 PUSH EAX 004C265D |. 56 PUSH ESI 004C265E |. 57 PUSH EDI 004C265F |. 69C0 68190000 IMUL EAX,EAX,1968 Конец сабрутины: Код (Text): 004C4DCB |. 5F POP EDI 004C4DCC |. 5E POP ESI 004C4DCD |. 5B POP EBX 004C4DCE |. 8BE5 MOV ESP,EBP 004C4DD0 |. 5D POP EBP 004C4DD1 \. C3 RETN Переделываем его в hex-редакторе: Код (Text): 004C4DCB |. 5F POP EDI 004C4DCC |. 5E POP ESI 004C4DCD |. 58 POP EAX 004C4DCE |. 8BE5 MOV ESP,EBP 004C4DD0 |. 5D POP EBP 004C4DD1 \. C3 RETN А в приведенном Вами фрагменте Код (Text): 004C4DCB . 5F POP EDI 004C4DCC . 5E POP ESI 004C4DCD . 58 POP EAX 004C4DCE . 8BE5 MOV ESP,EBP 004C4DD0 . 5D POP EBP 004C4DD1 . 68 C08E5D00 PUSH GameServ.005D8EC0 ; /FileName = "Addon.dll" 004C4DD6 . FF15 F4BC8C0C CALL DWORD PTR DS:[<&KERNEL32.LoadLibraryA>] ; \LoadLibraryA 004C4DDC . 09C0 OR EAX,EAX 004C4DDE . 0F84 B1610D00 JE GameServ.0059AF95 004C4DE4 . 68 F58E5D00 PUSH GameServ.005D8EF5 ; /ProcNameOrOrdinal = "CalChar" 004C4DE9 . 50 PUSH EAX ; |hModule 004C4DEA . FF15 F0BC8C0C CALL DWORD PTR DS:[<&KERNEL32.GetProcAddress>] ; \GetProcAddress 004C4DF0 . FFD0 CALL EAX 004C4DF2 . C3 RETN непонятно, где используется значение регистра EAX (искомый аргумент), который Вы успешно восстановили.
В приведенном коде что я использовал я загружаю из дллки свою ф-цию. Код (Text): extern "C" _declspec(dllexport) void CalChar(int aIndex) { } Входящим аргументом там идет int aIndex Чуствую что ошибаюсь... но мне казалось что int aIndex будет считан из стека. Видимо я не так понимал представление кода С++ в АСМе. Как тогда передать значение из EAX в вызываемую ф-цию? Изивиняюсь за мои знания... знаю они скудные...
Killbrum Тогда по адресу 004C4DD1 (сразу после pop ebp) нужно вставить инструкцию push eax, содержимое регистра будет аргументом для Вашей функции. А проверять лучше в дебуггере, по-крайней мере сразу увидите, что передается.
Killbrum Да, и еще нужно будет аккуратно оформить выход из сабрутины в случае ошибки фукнции LoadLibrary (снять лишний dword из стека - то самое значение, о котором мы тут толкуем).
Спасибо большое! Немножко потанцевали с бубном но теперь всё работает на УРА! Ещё раз очень и очень благодарю!
crypto ^_^ Да в принципе всё то что Вы написали оно и работает. Просто pop ebi поставили т.к. там надо было убрать одно значение из стека . Вот пожалуйста http://rapidshare.com/files/307965306/GameServer.rar.html