Исходные данные: Проект в с++ (x64) Подпрограмма на asm (x64) Необходимо: Перехватить вызовы стандартных WinAPI, например CreateWindowExW Заметки: Для x86 есть рабочая версия Сделать для x64 (inject x64 dll к x64 приложению проходит успешно) Общая стандартная схема работы подменной функции обработчика Код (Text): Intercept_CreateWindowExW { возвращаем запомненные байты на место стандартной функции вызываем стандартную функцию WinAPI CreateWindowExW перезаписываем первые байты стандартной функции вызовом нашей } для x86 (по стандартной схеме) - получаем адрес нашей новой функции обработчика Intercept_CreateWindowExW - кешируем 6 байт начала стандартной функции CreateWindowExW - на их место прописываем инструкцию перехода к нашей новой функции Код (C++): // push 0x11112222 (адрес нашей функции Intercept_CreateWindowExW) // ret id->newCode[0] = 0x68; id->newCode[4] = (HIWORD(newFunc) & 0xFF00) >> 8; id->newCode[3] = HIWORD(newFunc) & 0x00FF; id->newCode[2] = (LOWORD(newFunc) & 0xFF00) >> 8; id->newCode[1] = LOWORD(newFunc) & 0x00FF; id->newCode[5] = 0xC3; Для x86 все работает хорошо, задача адаптировать данный метод для x64, когда и приложения и все библиотеки скомпилены под x64 На данный момент получилось попасть в свою функцию Intercept_CreateWindowExW, но при выходе из нее приложение падает. Для x64 ассемблерная инструкция сейчас у меня выглядит следующим образом Код (C++): // push rdx // mov rdx, 0x1111222233334444 (адрес нашей функции Intercept_CreateWindowExW) // push rdx // ret // pop rdx id->newCode[0] = 0x52; id->newCode[1] = 0x48; id->newCode[2] = 0xba; id->newCode[3] = (newFunc & 0x00000000000000FF); id->newCode[4] = (newFunc & 0x000000000000FF00) >> 8; id->newCode[5] = (newFunc & 0x0000000000FF0000) >> 16; id->newCode[6] = (newFunc & 0x00000000FF000000) >> 24; id->newCode[7] = (newFunc & 0x000000FF00000000) >> 32; id->newCode[8] = (newFunc & 0x0000FF0000000000) >> 40; id->newCode[9] = (newFunc & 0x00FF000000000000) >> 48; id->newCode[10] = (newFunc & 0xFF00000000000000) >> 56; id->newCode[11] = 0x52; id->newCode[12] = 0xc3; id->newCode[13] = 0x5a; Данный код позволяет войти в свою функцию, отработать ее, но при выходе приложение падает, так как судя по всему ассемблерная инструкция должна быть иной. я попробовал вернуть rdx из стека сразу после попадания в свою функцию Intercept_CreateWindowExW, но приложение падает сразу на этом месте Подпрограмма на asm для x64 выглядит таким образом: Код (ASM): PUBLIC pop_rdx_asm .CODE pop_rdx_asm PROC pop rdх pop_rdx_asm ENDP END Подскажите, каким образом можно корректно вернуть управление после того как отработала своя функция. Как необходимо изменить asm инструкцию, в какую сторону смотреть?
Спасибо за наводку, Detours изучу. Есть желание не использовать сторонний код, а разобраться как оно работает, есть ощущение что не хватает совсем немного, но вот чего, пока ответа не нашел.
На самом деле, не хватает достаточно много. Твой способ вызова оригинальной функции нельзя использовать в многопоточных программах: пока один поток будет патчить функцию, другие потоки могут находиться на этом участке, который ты патчишь. Или потоки могут вызвать эту функцию, когда твой поток снял с неё перехват для вызова оригинала. Что касается прыжка на твой обработчик, ты можешь обойтись вообще без стека: Код (Text): ; Только для функций, которые не используют rax для передачи аргументов mov rax, addr jmp rax или Код (Text): jmp [rip+00h] FF 25 00 00 00 00 88 77 66 55 44 33 22 11 ; ^ jmp 0x1122334455667788 Посмотри, как сделано здесь, кода не много
zxbit, сделай проще == поставь патч на функу, а в хуке сделай развилку. То бишь чрез развилку вызываешь свой обработчик иль ориг функу по мере надобности.
Вот так "проще" - ему для этого нужен дизасм. А если тащить дизасм, то и развилки никакие не нужны - юзать классический способ с сохранённым оригинальным началом проще, чем городить условия.
зачем? всю ориг функу можно скопировать в другое место иль чрез параллельный процесс её вызывать (тормоза, конечно, но в ряде задач они вполне терпимы).
методом проб и ошибок == кои можно автоматизировать и реальный размер ориг функции получаешь за log2(N) шагов, где энн размер файла (где функа лежит).
??? Да никак это нельзя сделать по-другому, не анализируя длины инструкций и не разбирая, какие инструкции требуют патча после переноса в новое место
Ты же знаешь, что должна выдавать целевая функа на выходе == вот и критерий отработала ли функа правильно + если скопировал больше, чем весит ориг функа, она тоже будет работать. --- Сообщение объединено, 21 апр 2020 --- Другой Вопрос, для аких целей ТС лабает свои хуки == при мало-мальской защите кода такие подходы Абсолютно палевные и защита может просто тупо и тихо крашить такое.
Очевидно же, в учебных.. Сперва подмена функции обработчика, потом защита от подмены. UbIvItS, а Вы с какой целью интересуетесь?
Огромное всем спасибо, очень много полезной информации! Нужно это мне для того, чтобы mstsc расположить в своей оболочке, чтобы можно было организовать просмотр терминальных сессий пользователей в shadow режиме на одном экране.
zxbit, Всё не правильно. Тебе как сказали нужно функцию целиком в буфер собирать и потом туда отдавать управление из патча.
zxbit, Так про реализацию я и говорю. Этот detour поддерживается системой, но это очень кривой костыль необходимый исключительно для отладчика. Есть кучи жвижков которые умеют создавать и собирать граф(те перемещать код). Ручной мод применяется исключительно для хардкод блоков, тоесть не универсальных, а которые не изменяются и заранее известны. Если например в мод блоке ветвления или какая то привязка к текущему адресу(rip), то всё отвалится, не говоря уже про синхрон и наложение патчей. Да и вообще, зачем вручную забивать какие то байты, если есть универсальное решение в виде релокации.