Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.78/18: Рейтинг темы: голосов - 18, средняя оценка - 4.78
 Аватар для ASCII
99 / 70 / 13
Регистрация: 15.12.2013
Сообщений: 463

Выполнить перехват API вызова методом сплайсинга функции

28.08.2016, 20:47. Показов 3354. Ответов 8
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Общий!

Как следует из названия темы, хочу выполнить перехват API вызова.
Сделать это хочу методом сплайсинга функции.
То есть перезаписать первые 5 байт функции на:

Assembler
1
jmp near __address__
Тестовый стенд, это Visual C++, проект в релизе, перехватываемая функция:

C++
1
BOOL WINAPI CloseHandle(HANDLE);
В данном случае функция начинается вот так:

Assembler
1
2
3
76FEE080 8B FF                mov         edi,edi  
76FEE082 55                   push        ebp  
76FEE083 8B EC                mov         ebp,esp
Мы тут видим двухбайтовый nop, а также стандартное создание стекового фрейма в х32 вызовах функциях.
Как видно из опкодов, эти три инструкции занимают ровно 5 байт, а это именно то, что мне и нужно.

Но есть проблема:

Дело в том, что перехватываемая функция находится в .dll библиотеке kernel32.dll.
И вызов происходит примерно следующим образом:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
; ..............
; some stuff
; ..............
 
00000001: push eax ; handle to stack
00000002: call 001FFFFFh ; этот адрес я разумеется выдумал
 
; ..............
; some stuff
; ..............
 
; call прыгает сюда
; а отсюда прыгаем прямо в функцию
001FFFFF: jmp 000FFFFBh ; jmp near ==> 001FFFFFh + 000FFFFBh + 5h = 002FFFFFh
 
; ..............
; some stuff
; ..............
 
; то есть сюда
002FFFFF:  mov         edi,edi  
00300001:  push        ebp  
00300002:  mov         ebp,esp
То есть call прыгает на jmp, который прыгает в функцию...
Подозреваю, что это как-то связано с тем, что вызываемая функция находится в DLL.

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

Кто сталкивался с этим? Какие есть решения? Я вижу только лобовое решение.
Вычислить адрес начала функции, прибавив адрес, по которому расположена команда jmp с ее адресом + 5.
И уже там перезаписывать, но не костыль ли это?

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

main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <cstdlib>
#include "helper.hpp"
 
BOOL WINAPI Hook(HANDLE hObject)
{
    typedef BOOL (WINAPI *functionType)(HANDLE);
    
    static HookHelper::JmpNear jmpToOriginal;
    jmpToOriginal.opcode = 0xe9;
    jmpToOriginal.address = resolveRelAddress(&jmpToOriginal, &CloseHandle);
 
    if (!HookHelper::changePageProtection(&jmpToOriginal, sizeof(jmpToOriginal), PAGE_EXECUTE_READ))
    {
        std::wcerr << L"Cannot change protection for execute" << std::endl;
        std::wcin.get();
        std::quick_exit(GetLastError());
    }
 
    std::wcout << L"CloseHandle was hooked!" << std::endl;
 
    // call code which placed by the &jmpToOriginal address and pass hObject as parameter
    return HookHelper::call<functionType>(&jmpToOriginal, hObject);
}
 
int _tmain(int argc, TCHAR** argv)
{
    UNUSED(argc);
    UNUSED(argv);
 
    HookHelper::JmpNear* hookingFunction = reinterpret_cast<HookHelper::JmpNear*>(&CloseHandle);
 
    if (!HookHelper::changePageProtection(hookingFunction, sizeof(hookingFunction), PAGE_EXECUTE_READWRITE))
    {
        std::wcerr << L"Cannot change protection for execute and write" << std::endl;
        std::wcin.get();
        std::quick_exit(GetLastError());
    }
 
    hookingFunction->opcode = 0xe9;
    hookingFunction->address = resolveExistingRelAddress(&Hook, hookingFunction);
 
    HANDLE fileHandle = CreateFile(TEXT("d:\\test.txt"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    CloseHandle(fileHandle);
 
    std::wcin.get();
}
helper.hpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#pragma once
#include <algorithm>
#include <utility>
#include <cstdint>
#include <windows.h>
#include <tchar.h>
 
#define UNUSED(x) (void)x
 
namespace HookHelper
{
//============================================================================================
#pragma pack(push, 1)
struct JmpNear
{
    std::uint8_t opcode;
    std::int32_t address;
};
#pragma pack(pop)
 
using byte = std::uint8_t;
 
namespace
{
/*
*   mov edi, edi
*   push ebp
*   mov ebp, esp
*/
byte x32StackFrameOpcodes[] = { 0x8b, 0xff, 0x55, 0x8b, 0xec };
}
 
bool x32IsHookableStartOfFunction(void* pOffset)
{
    byte* comparingPointer = reinterpret_cast<byte*>(pOffset);
    return std::equal(x32StackFrameOpcodes, x32StackFrameOpcodes + sizeof(x32StackFrameOpcodes), comparingPointer);
}
 
std::int32_t resolveRelAddress(void* from, void* to)
{
    std::int32_t address =
        reinterpret_cast<byte*>(from)
        - reinterpret_cast<byte*>(to) + 5;
 
    if (from < to)
    {
        address = -address;
    }
 
    return address;
}
 
std::int32_t resolveExistingRelAddress(void* from, void* to)
{
    std::int32_t address =
        reinterpret_cast<byte*>(from)
        - reinterpret_cast<byte*>(to) - 5;
 
    return address;
}
 
bool changePageProtection(void* pOffset, std::uint32_t size, DWORD protection)
{
    DWORD oldProtection = 0;
    return VirtualProtect(pOffset, size, protection, &oldProtection) == TRUE;
}
 
template<typename T>
struct ResultOf;
 
template<typename ReturnType, typename... Args>
struct ResultOf <ReturnType(Args...)>
{
    using type = ReturnType;
};
 
template<typename ReturnType, typename... Args>
struct ResultOf <ReturnType(*)(Args...)>
{
    using type = ReturnType;
};
 
template<typename ReturnType, typename... Args>
struct ResultOf <ReturnType(WINAPI*)(Args...)>
{
    using type = ReturnType;
};
 
template<typename ToCast, typename... Args>
typename ResultOf<ToCast>::type
call(void* callable, Args&&... args)
{
    return reinterpret_cast<ToCast>(callable)(std::forward<Args&&>(args)...);
}
 
//============================================================================================
} // namespace
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
28.08.2016, 20:47
Ответы с готовыми решениями:

Перехват api-вызова. Ошибка при выполнении
Здравствуйте, есть dll-ка, которая успешно внедряется во все(или почти все) оконные приложения с помощью хуков. Суть ее в том, что она...

Перехват API функции
Доброго времени суток всем! Уважаемые форумчене помогите решить задачу, я взял функцию MessageBox и перехватил её. Объясните нубу как...

Перехват Win API функции
как перехватить АПИ функции , с помощью внедрения длл в процесс?

8
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
28.08.2016, 21:05
ASCII, а цель какая в данном случае?
Похукать функцию или самому последовательно пройтись по всем грабелькам,
связанным с этой темой? Если первое, тогда я советую не мучиться, а сразу
взять что-то готовое и проверенное, например MinHook (кстати, рекомендую).
Если второе, тогда тебе нужен дизассемблер, который умеет определять
длины инструкций и/или пропускать входные jmp...
1
 Аватар для ASCII
99 / 70 / 13
Регистрация: 15.12.2013
Сообщений: 463
28.08.2016, 21:13  [ТС]
Убежденный, вторая цель. А что ты имеешь ввиду под дизассемблером, умеющим пропускать инструкции? Я просто в данном случае подумал написать свой минифреймворк для этого, хотя-бы для x32. То есть в любом случае придется самому делать проверку на опкоды?

Что-то вроде:

C++
1
2
3
4
5
6
7
8
9
if(instruction == jmp)
{
      // calculate address to the real start of function
}
else if(instruction == stackFrame)
{
    int sz = calculateBytesForNops();
    fill(&instruction, sz);
}
Добавлено через 47 секунд
Мне кажется в качестве обучающей цели, написать свой фреймворк для этого, весьма неплохо было бы? Пусть даже без поддержки х64.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
28.08.2016, 21:15
Цитата Сообщение от ASCII Посмотреть сообщение
То есть в любом случае придется самому делать проверку на опкоды?
Да, от этого никуда не уйти.
Начальные байты функции могут быть очень разные, в некоторых ситуациях
там можно натолкнуться на чужие хуки (в том числе виндовые - apphelp.dll),
так что придется учесть множество всяких "если". В x64 все еще сложнее:
RIP-адресация, большое адресное пространство, больший размер инструкций,
для которых не всегда есть место, и т.д.
0
 Аватар для ASCII
99 / 70 / 13
Регистрация: 15.12.2013
Сообщений: 463
28.08.2016, 21:20  [ТС]
Убежденный, а вообще, вот данная ситуация является вполне стандартным случаем вызова функции? Есть какая-нибудь систематика тут? Где будет стоять jmp на функцию, а где сразу call передает управление функции?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
28.08.2016, 21:46
Это вполне типично, что из kernel32 идет jmp в kernelbase, а оттуда еще куда-нибудь.
Но никаких жестких правил нету, начальные байты функции теоретически могут
быть совершенно любыми [опкодами].
1
139 / 139 / 53
Регистрация: 14.06.2016
Сообщений: 467
28.08.2016, 22:01
Может быть я не совсем понял сабж, в чём проблема восстановить первые 5 байт, вызывать оригинальную функцию, затем установить свой джамп на хук обратно?
0
 Аватар для ASCII
99 / 70 / 13
Регистрация: 15.12.2013
Сообщений: 463
28.08.2016, 22:04  [ТС]
jr_, дело в том, что это требует повторной перезаписи тела функции. А хочется достичь указанного поведения. Установить хук, а при желании передать управление оригинальной функции, прибавить к адресу 5 байт и просто ее вызвать. И где тогда опять устанавливать хук, если его постоянно снимать?
0
 Аватар для iDReeM
64 / 68 / 18
Регистрация: 21.10.2015
Сообщений: 228
29.08.2016, 02:01
омайгайд.

невероятная шляпа
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void* Create_Hook(BYTE *src, const BYTE *dst, const int len)
{
    BYTE *jmp;
    DWORD dwback;
    DWORD jumpto, newjump;
 
    VirtualProtect(src,len,PAGE_READWRITE,&dwback);
    if(src[0] == 0xE9)
    {
        jmp = (BYTE*)malloc(10);
        jumpto = (*(DWORD*)(src+1))+((DWORD)src)+5;
        newjump = (jumpto-(DWORD)(jmp+5));
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = newjump;
        jmp += 5;
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = (DWORD)(src-jmp);
    }
    else
    {
        jmp = (BYTE*)malloc(5+len);
        memcpy(jmp,src,len);
        jmp += len;
        jmp[0] = 0xE9;
        *(DWORD*)(jmp+1) = (DWORD)(src+len-jmp)-5;
    }
    src[0] = 0xE9;
    *(DWORD*)(src+1) = (DWORD)(dst - src) - 5;
    for(int i = 5; i < len; i++)
        src[i] = 0x90;
    VirtualProtect(src,len,dwback,&dwback);
    return (jmp-len);
}
крэк с комочками

C++
1
2
3
4
5
6
7
8
typedef BOOL (WINAPI* HookCloseHandle)(__in HANDLE);
HookCloseHandle pHookCloseHandle;
 
BOOL WINAPI myCloseHandle(__in HANDLE hObject)
{
    //ТРАЛЯЛЯ МАЖИК Твои извращения
    return pHookCloseHandle(hObject);//тут ты возвращаешь оригинал
}
установить хук:
C++
1
pHookCloseHandle = (HookCloseHandle)Create_Hook(PBYTE(::CloseHandle), (BYTE*)myCloseHandle, 5);
Добавлено через 27 минут
Цитата Сообщение от ASCII Посмотреть сообщение
То есть call прыгает на jmp, который прыгает в функцию...
даже в таком случае, можно отредактировать Asm инструкцию невероятной шляпой что указана выше. (подменив на свою)

штукенция
C++
1
2
3
4
5
6
7
8
9
DWORD AddressToCall = 0x002FFFFF;//адресс из твоего примера
_declspec( naked ) int yourHook()
{
    __asm
    {
        push eax 
            call AddressToCall //вызываем функцию, а не прыжок.
    }
}
установка
C++
1
2
DWORD AddressToChangeASM = 0x0;//адресс по которому меняем инструкцию
Create_Hook((BYTE*)AddressToChangeASM, (BYTE*)yourHook, 5); //где 5 - размер записи в байтах.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
29.08.2016, 02:01
Помогаю со студенческими работами здесь

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

Перехват вызова функций из сторонней DLL
Всем добрый день. играя в Lineage 2, появилась идейка написания своего собственного &quot;бота&quot;. знаком с C++, C#,WinApi ... но... ...

Перехват стандартного вызова запущенной из ява-приложения внешней программы
Как перехватить стандартный вывод запущенной из ява-приложения внешней программы?

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

Перехват API
Сразу код ) DWORD dwProtect = PAGE_READWRITE; BYTE old; BYTE * fPtr; #pragma pack(push, 1)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru