Сейчас на форуме: Rio, tyns777, zombi-vadim (+7 невидимых)

 eXeL@B —› Программирование —› Посоветуйте как лучше сделать перехват функции
Посл.ответ Сообщение

Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 13:53
· Личное сообщение · #1

Добрый день!
Посоветуйте, пожалуйста, оптимальное решение данной задачи.

Есть процесс, который ломится в сеть по определенному адресу (пакеты идут по UDP). Этот адрес нужно подменить на свой. Файл Hosts не подходит потому что запрос идет на IP.
Сделал внедрение своей DLL в память этого процесса, в главном потоке библиотеки нахожу адрес:
Code:
  1. Orig_addr := GetProcAddress(hWinsock, 'sendto');

Далее обычный сплайсинг - по этому адресу считываю 6 байт оригинального кода и прописываю туда PUSH ADDR + RET. Работает все с точностью до многопоточности - при ней часто вылетает из-за нарушения последовательности чтения-записи этих байт.

Вариант 1 каждый раз при вызове моей функции sendto входить в CriticalSection, но мне кажется что это лишняя нагрузка на машину и тормоза при большом количестве пакетов. Или я не прав, делать так и не париться?


Вариант 2 зная что нужные мне вызовы функции sendto() происходят только НЕ из главного потока программы, а большинство пакетов отправляются как раз из главного (выяснил опытным путем логирования GetCurrentThreadId() и дампа данных) каким-то образом пройтись по памяти нужных потоков и позаменять адрес Orig_addr на адрес моей новой функции. Таким образом, при вызове каждый раз не будет писаться/возвращаться патч оригинальной функции, будет сразу идти моя функция.
Возникают 2 нубских вопроса:
1) имеет ли этот вариант право на жизнь?
2) как определить начальный и конечный адрес памяти, в которой находится машинный код того или иного потока процесса?
3) поиск-замена 4 байт памяти (адрес) чревата коллизиями, лучше искать инструкцию call + нужный адрес и подобные. Где взять полный список этих сигнатур (опкодов)? в АСМе я, к сожалению, не силен.

Спасибо.

P.S. за Delphi прошу не троллить




Ранг: 1053.6 (!!!!), 1078thx
Активность: 1.060.81
Статус: Участник

Создано: 01 февраля 2015 13:58
· Личное сообщение · #2

в гугле вбей
delphi splacing
и читай



Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 14:10 · Поправил: Hellrider
· Личное сообщение · #3

Ну я ж не настолько нуб, сделано по примерам, найденным в гугле. Только инжект чуть по-другому.

Вопрос будет ли EnterCriticalSection / LeaveCriticalSection сильно нагружать систему и реально ли сделать второе.
Я работал с памятью процесса (поиск-замена значения переменной (hwid) для привязки ключа от другого ПК) через VirtualQueryEx(), но там память процесса, и передавать надо хендл процесса, как получить для отдельного потока не знаю.

P.S. Splicing или splacing? все говорят сплайсинг а не сплэйсинг. Или я ошибаюсь?



Ранг: 419.0 (мудрец), 647thx
Активность: 0.460.51
Статус: Участник
"Тибериумный реверсинг"

Создано: 01 февраля 2015 14:41 · Поправил: ELF_7719116
· Личное сообщение · #4

Hellrider
давайте попробуем зайти еще с другой стороны!
программа(процесс, которые имеете ввиду) проверяет свою целостность? навешаны ли какие либо пакеры?
если ничего подобного нет, можно спокойно подправить функционал внутри самой целевой проги, тем самым решив проблему многопоточности (все изначально будет в рабочем состоянии). тут несколько вариантов:
1. Исправить непосредственно в самом exe файле. Придется выучить немного асм и знать принципы отладки.
2. Написать свой аналог winsock библиотеки на том-же Delphi и подправить таблицу импорта exe - редирект на Вашу библиотеку. Тут достаточно знания работы утилиты PeTools и понимания, что именно нужно править.
Hellrider пишет:
Работает все с точностью до многопоточности - при ней часто вылетает из-за нарушения последовательности чтения-записи этих байт.

Вообще же, по идеи, нужно остановить все потоки в программе, перед тем как писать через WriteProcessmemory. К тому же может стоять какая-то защита(протектор).



Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 15:52 · Поправил: Hellrider
· Личное сообщение · #5

Спасибо за ответ. На самом деле все несколько сложнее.
В самой программе защиты нет никакой, целостность не проверяется. Есть 2 библиотеки - одна ответственная за отправку нужного мне запроса (пакета данных), но за сам адрес хоста отвечает другая библиотека, а прямой связи между ними я не нашел (делается через основную программу или еще одну библиотеку). Там много мистики и все слишком запутано. Они тоже не зашифрованы. НО ip адрес ни в виде строки, ни в виде integer я нигде не нашел. В древних версиях этой библиотеки он был прямым текстом и патчился HEX-редактором. Они не совместимы с новыми версиями основной программы, которая мне и нужна, более того у более новых версий они не совместимы между собой (краш). Именно поэтому я и решил делать универсальный перехват вызова.

Сделать фейковый клон Winsock и подправить таблицу импорта в HEX я чего-то не подумал, хотя именно через фейк и внедряюсь в процесс чтобы не возникало лишних вопросов у конечных пользователей. В библиотеке, из которой идет запрос, действительно есть в таблице импорта WSOCK32.dll и 15 функций по индексу, без имен. Думаю, достаточно сделать именно их и это будет решением, которое, скорее всего, не повлияет на работу основной программы. Пошел делать, сложность низкая, если что будет для каждой версии свой патч.

Но решением только для самой последней версии софта. На предыдущей (она тоже нужна) не прокатит т.к. из WSOCK32 импортируется только 3 функции (htonl, ntohl, gethostbyname), а это явно не достаточно для отправки пакета, в тексте упоминание sendto отсутствует, следовательно фиг я смогу разобраться где его искать и подменять. Winsock2 там вообще не используется, хотя ИМХО уже должны были на него перейти т.к. последние версии переделаны капитально относительно старой линейки.

Список потоков получить можно, останавливать не пробовал но это решаемо. Тогда еще вопрос к опытным специалистам - это не будет тормозить? EnterCriticalSection по сути будет выполнять ту же функцию - не давать другим потокам одновременно выполнять этот же участок кода. Что будет меньше нагружать систему?

=====
Сделал фейковый WSOCK32 - облом. Вызовы идут исключительно из одного потока, хотя в "исправляемой" библиотеке их два. Это второй, он не нужен. Получается, что из этой библиотеки нужный мне пакет отправляется одним методом, не использующим таблицу импорта, а другой поток уже ее использует. С учетом этого и того что в других версиях импорта нужных функций вообще нет - метод не катит.
Нужно возвращаться к предыдущему:

- EnterCriticalSection
- Остановка потоков
- Патч адреса функции во всех местах откуда она вызывается

Что из первых 2х лучше и реально ли сделать третье?




Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 01 февраля 2015 18:12
· Личное сообщение · #6

Hellrider пишет:
- EnterCriticalSection
- Остановка потоков
- Патч адреса функции во всех местах откуда она вызывается

- есть доля истины. тормоза хз, насколько критичны
- как оно работать будет?
- те же яйки, ломиться они все равно будут в новую функу всеми

а если кода нет в функции, а сразу прыжок на оригинальную - не валится?

-----
От многой мудрости много скорби, и умножающий знание умножает печаль




Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 18:52
· Личное сообщение · #7

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

Если кода нет вылеты тоже есть, на первой минуте работы когда трафик наиболее интенсивный. Я уже проверял это - вызывается именно наложением записей первых байт кода функции




Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 01 февраля 2015 20:23
· Личное сообщение · #8

Hellrider пишет:
чтобы никто не заменял первые 6 байт

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

приведите код. гадать не хочу

-----
От многой мудрости много скорби, и умножающий знание умножает печаль





Ранг: 324.3 (мудрец), 222thx
Активность: 0.480.37
Статус: Участник

Создано: 01 февраля 2015 20:31
· Личное сообщение · #9

Hellrider пишет:
Остановка потоков - все потоки стоят кроме текущего чтобы никто не заменял первые 6 байт функции

Бред! Изменил один раз и больше не надо, перехватывай...

Hellrider пишет:
остановка потоков все равно должна быть через критическую сессию или семафор

Ещё один бред...

-----
IZ.RU




Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 20:38
· Личное сообщение · #10

Бред! Изменил один раз и больше не надо
Это как? восстановил - запустил - вернул подмену! Это же сплайсинг.
Пока такой рабочий вариант
Code:
  1. function NewSendto(s: TSocket; var Buf; len, flags: Integer; var addrto: TSockAddr; tolen: Integer): Integer; stdcall;
  2. var
  3.      Written: dword;
  4. begin
  5.      EnterCriticalSection(CriticalSection);
  6.      WriteProcessMemory(INVALID_HANDLE_VALUE, SToAdr, @OldSTo, SizeOf(OldCode), Written);
  7.      Result := Sendto(s, Buf, len, flags, addrto, tolen);
  8.      {$IFDEF DEBUG}log('SendTo LEN= '+inttostr(len)+' ADDR='+inet_ntoa(addrto.sin_addr)+':'+inttostr(ntohs(addrto.sin_port))+' returned '+inttostr(Result));{$ENDIF}
  9.      WriteProcessMemory(INVALID_HANDLE_VALUE, SToAdr, @JmpSTo, SizeOf(far_jmp), Written);
  10.      LeaveCriticalSection(CriticalSection);
  11. end;

Надо бы конечно на тестовом приложении замерить время отправки 1000 пакетов по 1кб на 127.0.0.1 с CriticalSection и без нее...




Ранг: 337.6 (мудрец), 224thx
Активность: 0.210.1
Статус: Участник
born to be evil

Создано: 01 февраля 2015 20:44 · Поправил: ajax
· Личное сообщение · #11

Hellrider
даже на ум _такое_ не могло прийти курите гугль, хуков на дельфи полно

-----
От многой мудрости много скорби, и умножающий знание умножает печаль





Ранг: 324.3 (мудрец), 222thx
Активность: 0.480.37
Статус: Участник

Создано: 01 февраля 2015 21:55 · Поправил: DenCoder
· Личное сообщение · #12

Hellrider пишет:
восстановил - запустил - вернул подмену!

Зачем возвращать подмену? Это только тормозит процесс, лишняя последовательность действий! Так конечно, при таком подходе тормозить треды надо. Но зачем?

Перехватил - передал управление в свой код - обработал сиутацию - выполнил оригинальные инструкции - передал управление обратно

-----
IZ.RU




Ранг: 3.6 (гость), 1thx
Активность: 0.010
Статус: Участник

Создано: 01 февраля 2015 22:10
· Личное сообщение · #13

DenCoder пишет:
Перехватил - передал управление в свой код - обработал сиутацию - выполнил оригинальные инструкции - передал управление обратно

Исходное приложение вызывает исходную функцию SendTo() в которой прописан переход в мою функцию.
Моя функция не может выполнить исходную функцию как есть потому что будет зацикливание - надо туда вернуть исходные байты. Я это делаю, потом вызываю исходную, она возвращает результат. Если я просто выйду - при следующем вызове sendto() из программы там уже не будет перехода на мою функцию. Мне не надо разово, надо хватать определенные пакеты за все время работы программы, потому в конце туда (обратно) прописывается переход на мою функцию. Во всех примерах так вроде, только было 2 способа - с абсолютным адресом и относительным



Ранг: 42.2 (посетитель), 42thx
Активность: 0.040
Статус: Участник

Создано: 01 февраля 2015 23:37
· Личное сообщение · #14

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

Угадайте почему там используется дизассемблер длины инструкций?




Ранг: 324.3 (мудрец), 222thx
Активность: 0.480.37
Статус: Участник

Создано: 02 февраля 2015 08:37
· Личное сообщение · #15

Hellrider пишет:
Моя функция не может выполнить исходную функцию как есть потому что будет зацикливание

Разве кто-нибудь говорил, что передать управление обратно надо на начало? Распространённый случай, когда перехватываемая функция начинается с
Code:
  1. func1:
  2. mov edi, edi
  3. push ebp
  4. mov ebp, esp
  5.  
  6. func1_entry_from_hook:
  7. ...
  8. ...

Перед заменой этих байт всего лишь надо их вставить в конец обработчика
Code:
  1. func1_hook:
  2. ...
  3.  
  4. func1_hook_end:
  5. push ebp
  6. mov ebp, esp
  7. jmp func1_entry_from_hook


Конечно, как сказал kid, можно сделать более универсально, использовав дизассеблер длин, поскольку не всегда функция начинается с этих байт.

-----
IZ.RU



 eXeL@B —› Программирование —› Посоветуйте как лучше сделать перехват функции
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати