Перехват стандартных WinAPI под x64

Тема в разделе "WASM.X64", создана пользователем zxbit, 20 апр 2020.

  1. zxbit

    zxbit New Member

    Публикаций:
    0
    Регистрация:
    20 апр 2020
    Сообщения:
    4
    Исходные данные:
    Проект в с++ (x64)
    Подпрограмма на asm (x64)

    Необходимо:
    Перехватить вызовы стандартных WinAPI, например CreateWindowExW

    Заметки:
    Для x86 есть рабочая версия
    Сделать для x64
    (inject x64 dll к x64 приложению проходит успешно)


    Общая стандартная схема работы подменной функции обработчика
    Код (Text):
    1. Intercept_CreateWindowExW
    2. {
    3. возвращаем запомненные байты на место стандартной функции
    4. вызываем стандартную функцию WinAPI CreateWindowExW
    5. перезаписываем первые байты стандартной функции вызовом нашей
    6. }

    для x86 (по стандартной схеме)
    - получаем адрес нашей новой функции обработчика Intercept_CreateWindowExW
    - кешируем 6 байт начала стандартной функции CreateWindowExW
    - на их место прописываем инструкцию перехода к нашей новой функции

    Код (C++):
    1. // push 0x11112222 (адрес нашей функции Intercept_CreateWindowExW)
    2. // ret
    3.  
    4. id->newCode[0] = 0x68;
    5. id->newCode[4] = (HIWORD(newFunc) & 0xFF00) >> 8;
    6. id->newCode[3] = HIWORD(newFunc) & 0x00FF;
    7. id->newCode[2] = (LOWORD(newFunc) & 0xFF00) >> 8;
    8. id->newCode[1] = LOWORD(newFunc) & 0x00FF;
    9. id->newCode[5] = 0xC3;
    Для x86 все работает хорошо, задача адаптировать данный метод для x64, когда и приложения и все библиотеки скомпилены под x64


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

    Для x64 ассемблерная инструкция сейчас у меня выглядит следующим образом

    Код (C++):
    1. // push rdx
    2. // mov rdx, 0x1111222233334444 (адрес нашей функции Intercept_CreateWindowExW)
    3. // push rdx
    4. // ret
    5. // pop rdx
    6.  
    7. id->newCode[0] = 0x52;
    8. id->newCode[1] = 0x48;
    9. id->newCode[2] = 0xba;
    10. id->newCode[3] = (newFunc & 0x00000000000000FF);
    11. id->newCode[4] = (newFunc & 0x000000000000FF00) >> 8;
    12. id->newCode[5] = (newFunc & 0x0000000000FF0000) >> 16;
    13. id->newCode[6] = (newFunc & 0x00000000FF000000) >> 24;
    14. id->newCode[7] = (newFunc & 0x000000FF00000000) >> 32;
    15. id->newCode[8] = (newFunc & 0x0000FF0000000000) >> 40;
    16. id->newCode[9] = (newFunc & 0x00FF000000000000) >> 48;
    17. id->newCode[10] = (newFunc & 0xFF00000000000000) >> 56;
    18. id->newCode[11] = 0x52;
    19. id->newCode[12] = 0xc3;
    20. id->newCode[13] = 0x5a;
    Данный код позволяет войти в свою функцию, отработать ее, но при выходе приложение падает, так как судя по всему ассемблерная инструкция должна быть иной. я попробовал вернуть rdx из стека сразу после попадания в свою функцию Intercept_CreateWindowExW, но приложение падает сразу на этом месте

    Подпрограмма на asm для x64 выглядит таким образом:

    Код (ASM):
    1. PUBLIC pop_rdx_asm
    2.  
    3. .CODE
    4. pop_rdx_asm PROC
    5.      pop rdх
    6. pop_rdx_asm ENDP
    7. END

    Подскажите, каким образом можно корректно вернуть управление после того как отработала своя функция. Как необходимо изменить asm инструкцию, в какую сторону смотреть?
     
    Последнее редактирование: 21 апр 2020
  2. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    113
    а то мешает юзать detours от мелкомягких?
     
    zxbit и M0rg0t нравится это.
  3. zxbit

    zxbit New Member

    Публикаций:
    0
    Регистрация:
    20 апр 2020
    Сообщения:
    4
    Спасибо за наводку, Detours изучу. Есть желание не использовать сторонний код, а разобраться как оно работает, есть ощущение что не хватает совсем немного, но вот чего, пока ответа не нашел.
     
  4. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    На самом деле, не хватает достаточно много.
    Твой способ вызова оригинальной функции нельзя использовать в многопоточных программах: пока один поток будет патчить функцию, другие потоки могут находиться на этом участке, который ты патчишь.
    Или потоки могут вызвать эту функцию, когда твой поток снял с неё перехват для вызова оригинала.

    Что касается прыжка на твой обработчик, ты можешь обойтись вообще без стека:
    Код (Text):
    1.  
    2. ; Только для функций, которые не используют rax для передачи аргументов
    3. mov rax, addr
    4. jmp rax
    5.  
    или
    Код (Text):
    1.  
    2. jmp [rip+00h]
    3. FF 25 00 00 00 00 88 77 66 55 44 33 22 11
    4. ; ^  jmp 0x1122334455667788
    5.  
    Посмотри, как сделано здесь, кода не много
     
    zxbit нравится это.
  5. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    zxbit, сделай проще == поставь патч на функу, а в хуке сделай развилку. То бишь чрез развилку вызываешь свой обработчик иль ориг функу по мере надобности. :)
     
  6. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Вот так "проще" - ему для этого нужен дизасм. А если тащить дизасм, то и развилки никакие не нужны - юзать классический способ с сохранённым оригинальным началом проще, чем городить условия.
     
  7. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    зачем? всю ориг функу можно скопировать в другое место иль чрез параллельный процесс её вызывать (тормоза, конечно, но в ряде задач они вполне терпимы).
     
  8. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    А как он без дизасма будет её копировать и как узнает размер функции?
     
  9. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    методом проб и ошибок == кои можно автоматизировать и реальный размер ориг функции получаешь за log2(N) шагов, где энн размер файла (где функа лежит).
     
  10. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    ???
    Да никак это нельзя сделать по-другому, не анализируя длины инструкций и не разбирая, какие инструкции требуют патча после переноса в новое место
     
  11. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    Ты же знаешь, что должна выдавать целевая функа на выходе == вот и критерий отработала ли функа правильно + если скопировал больше, чем весит ориг функа, она тоже будет работать. :grin:
    --- Сообщение объединено, 21 апр 2020 ---
    Другой Вопрос, для аких целей ТС лабает свои хуки == при мало-мальской защите кода такие подходы Абсолютно палевные и защита может просто тупо и тихо крашить такое.
     
  12. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.787
    Очевидно же, в учебных.. Сперва подмена функции обработчика, потом защита от подмены.
    UbIvItS,
    а Вы с какой целью интересуетесь? :)
     
    zxbit нравится это.
  13. UbIvItS

    UbIvItS Well-Known Member

    Публикаций:
    0
    Регистрация:
    5 янв 2007
    Сообщения:
    6.243
    :)
     
    Mikl___ нравится это.
  14. FoxB

    FoxB Member

    Публикаций:
    0
    Регистрация:
    10 июл 2003
    Сообщения:
    113
    ну так оно идет в сырцах - хоть заизучайтесь...
    https://github.com/Microsoft/Detours
     
    M0rg0t нравится это.
  15. zxbit

    zxbit New Member

    Публикаций:
    0
    Регистрация:
    20 апр 2020
    Сообщения:
    4
    Огромное всем спасибо, очень много полезной информации! Нужно это мне для того, чтобы mstsc расположить в своей оболочке, чтобы можно было организовать просмотр терминальных сессий пользователей в shadow режиме на одном экране.
     
  16. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    zxbit,

    Всё не правильно. Тебе как сказали нужно функцию целиком в буфер собирать и потом туда отдавать управление из патча.
     
  17. zxbit

    zxbit New Member

    Публикаций:
    0
    Регистрация:
    20 апр 2020
    Сообщения:
    4
    Это я про целевую задачу написал. А по реализации идею понял, буду пробовать!
     
  18. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    zxbit,

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