Assembler Делаем инжект в функцию

Статус
В этой теме нельзя размещать новые ответы.

MogAika

Известный
Автор темы
Друг
237
441
Примечание: Для дизассемблирования кода понадобится любая из этих программ: OllyDbg, Cheat Engine, Ida Pro, SoftICE. Я буду использовать Cheat Engine.
В процессе урока мы заинжектимся из нашей длл в функцию samp.dll AddChatMessage(...), где сможем изменить текст, который приходит с сервера.
Для начала нам понадобится смещение функции, а так же её входящие аргументы. Обычно я их добываю сам, но вы можете найти большое их количество в исходниках собейта.
0x79700 ; int __stdcall addClientMessage(DWORD color, LPSTR str)
Глянем начало функции в дизассемблере (откроем гта, откроем cheat engine, нажмём MemoryView в ce, жмакаем Ctrl+G, там вводим samp.dll+79700, жмакаем Enter):
Код:
samp.dll+79700 - 56                    - push esi
samp.dll+79701 - 8B 74 24 0C          - mov esi,[esp+0C]
samp.dll+79705 - 8B C6                - mov eax,esi
samp.dll+79707 - 57                    - push edi
samp.dll+79708 - 8B F9                - mov edi,ecx
Вспоминаем что нам понадобится 5 байтов для инжекта, их у нас занимают 2 опкода, которые легко подменить.
Напишем функцию для инжекта, я использую c++:
Код:
void InjectJump(DWORD _offset, DWORD target)
{
  unsigned long Protection;
  VirtualProtect((void*)_offset, 5, PAGE_EXECUTE_READWRITE, &Protection);
  target -= (_offset + 5);
  *((char*)_offset) = 0xE9;
  memcpy((LPVOID)(_offset+1), &target, sizeof(DWORD));
  VirtualProtect((void*)_offset, 5, Protection, 0);
}
Для примера сделаем функцию, которая добавит в конец текста строку "Injected".
Код:
VOID _stdcall AddClientMessageCallback(DWORD color, LPSTR str) //не забываем _stdcall, иначе компилятор может подкинуть нам свинью
{
  strcat_s(str, 256, " Injected");
}
Теперь нам надо сделать "прослойку", можно конечно обойтись без неё, но это сильно упрощает жизнь, при этом потери в скорости очень незначительны.
Задача прослойки: сохранить регистры, вызвать функцию (AddClientMessageCallback), восстановить регистры, вызвать опкоды, которые мы подменили инжектом (5 байт).
Для того, чтобы компилятор не добавил своего говно-кода, мы пропишем _declspec( naked ) перед функцией
Код:
LPVOID _injectedAddClientMessageCallbackRetAddr; //Нужно указать при создании инжекта, чтобы функция знала, куда ей возвращаться
_declspec( naked ) VOID _injectedAddClientMessageCallback()
{
  _asm
  {
  pushad//Сохраняем регистры
 
  //Количество пушей - это количество аргументов функции
  //Смещение в стэке расчитывается так: (кол-во аргументов)*4+0x20
  //У нас получается 2*4=8
  push [esp+0x28]//строка
  push [esp+0x28]//цвет
  call AddClientMessageCallback
 
  popad  //Восстанавливаем регистры
 
  //Ниже повторяем действия, которые мы подменили
  push esi
  mov esi,[esp+0xC]
 
  //Возвращаемся в функцию
  jmp _injectedAddClientMessageCallbackRetAddr
  }
}
Осталось только прописать инжект в памяти при запуске
Код:
  _injectedAddClientMessageCallbackRetAddr = (LPVOID)(dwSAMP+0x79705); //0x79700 + 5 конец опкода, которые мы перезаписываем
  InjectJump(dwSAMP+0x79700, (DWORD) &_injectedAddClientMessageCallback);
В итоге получили:
sa-mp-026.png
 

alfazlo

Потрачен
128
36
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Сложно, сложно, можешь сделать на cleo такойже код с описанием, а то C++ не понятно. Как я понял
void InjectJump(DWORD _offset, DWORD target) - это универсальная функция которая инжектит jump по оффсету? Сложно на C++ все выглядит.
Еще
push [esp+0x28]//строка
push [esp+0x28]//цвет
call AddClientMessageCallback - это мы заносим в стэк оригинальную строку и цвет и они передается как параметры в функцию AddClientMessageCallback ?, а почему функция берет значения именно из стэка, а не из регистров, или еще чего-нибудь и зачем нужно сохранять и восстанавливать регистры и делать возврат в оригинальную функцию если мы скопировали ее тело в инжект функцию? Можно сделать завершение в инжект функции? Ааа кажись догнал, мы скопировали только замененные 5 байт, спасибо за теорию.
 

MogAika

Известный
Автор темы
Друг
237
441
push [esp+0x28]//строка
push [esp+0x28]//цвет
call AddClientMessageCallback - это мы заносим в стэк оригинальную строку и цвет и они передается как параметры в функцию
AddClientMessageCallback ?
Да. Вообще можно обойтись без вызова AddClientMessageCallback, однако с этим будет много мороки, да и листинг из урока устойчивее к изменениям кода (не раз сталкивался с глупыми, сложно-искаемыми ошибками, которые тут фильтруются).
а почему функция берет значения именно из стэка, а не из регистров, или еще чего-нибудь и зачем нужно сохранять и восстанавливать регистры и делать возврат в оригинальную функцию если мы скопировали ее тело в инжект функцию?Можно сделать завершение в инжект функции?
Данные мы берем из стека, т.к. по любому соглашению вызовов данные передаются именно через него.
Сохранять и восстанавливать регистры нужно для того, что бы наша функция AddClientMessageCallback случаем не поменяла их, что может влечь за собой вылет (конечно это происходит далеко не часто, но лучше не пренебрегать этим).
Мы скопировали не тело функции, а только замененные 5 байт. Если бы мы в функции AddClientMessageCallback сделали полную альтернативу функции-жертвы, то тогда можно было бы заменить прыжок на выход. (если сделать это тут, то сообщения просто не будут добавляться)
Код:
push esi
mov esi,[esp+0xC]
jmp _injectedAddClientMessageCallbackRetAddr
на
Код:
ret (кол-во параметров)*4 //Если функция _stdcall
либо
Код:
retn //Если функция _cdecl
 
  • Нравится
Реакции: Vintik и alfazlo
Статус
В этой теме нельзя размещать новые ответы.