Переключение на Главную Страницу Страницы: [1] 2  ОтправитьПечать
Очень популярная тема (более 25 ответов) Перехват функций (число прочтений - 12977 )
PavelS
Junior Member
**
Отсутствует


I Love YaBB 2!

Сообщений: 16
Зарегистрирован: 14. Декабря 2006
Перехват функций
16. Декабря 2006 :: 02:20
Печать  
Написал чуток кода по перехвату функций вызовов DLL. Его навалом в 1С++, только в том и дело, что он там выглядит убого, по-хакерски без комментариев и вообще жестоко, т.к. там сплошные указатели, в которых запутаться - 1 секунда. Копирование циклом 6-и байт for(i = 0; i<6;i++)... меня добило. Может, так будет кому полезней? (да, что это не лучший способ перехвата - мне известно, но так уж повелось что он применяется в 1С++ повсеместно)

_______________
//SFnOverrideStruct.hpp
// License: free, not Gnu!
#ifndef SFNOVERRIDESTRUCT_HPP
#define SFNOVERRIDESTRUCT_HPP
#if USE_PRAGMA_ONCE
#pragma once
#endif

struct SFnOverrideStruct
{
  struct SSixBytes
  {
    BYTE m_aBytes[6];
  };
  BOOL ProcessOverride(TCHAR* p_acDllName,
    char* p_acFunctionName,
    FARPROC p_pFnNew);
  void Restore();
  void QuickOverride();

  SSixBytes m_sixbytesOldBytes;
  FARPROC m_pFunctionPointerOld;
  FARPROC m_pFunctionPointerNew;
};

#endif //SFNOVERRIDESTRUCT_HPP

_________________
//SFnOverrideStruct.cpp
// License: free, not Gnu!
#include "stdafx.hpp"
#include "SFnOverrideStruct.hpp"

#define OPCODE_RELATIVE_JMP 0x25FF

///////////////////////////////////////////////////////////////////////////////
// Function :     ProcessOverride
// Author :       Suslo P.
// Date :         16.12.2006
//-----------------------------------------------------------------------------
// Parameters :
//  [in] TCHAR* p_acDllName - dll name
//  [in] char* p_acFunctionName - function name
//  [in] FARPROC p_pFnNew - n_e_w function pointer
// Description : overrides function p_acFunctionName of DLL p_acDllName by
//     p_pFnNew
// License: free, not Gnu!
// Output :  nonzero on success, otherwise 0
///////////////////////////////////////////////////////////////////////////////
BOOL SFnOverrideStruct:ЯзыкrocessOverride(TCHAR* p_acDllName,
                                       char* p_acFunctionName,
                                       FARPROC p_pFnNew)
{
  //--- fill the struct
  m_pFunctionPointerNew = (FARPROC)p_pFnNew;
  HINSTANCE l_hMouleHandle=GetModuleHandle(p_acDllName);
     DWORD oldProtect;
     m_pFunctionPointerOld=GetProcAddress(l_hMouleHandle, p_acFunctionName);
     m_sixbytesOldBytes = *(SSixBytes*)m_pFunctionPointerOld;
  //--- override
     VirtualProtect(m_pFunctionPointerOld,
    6,
    PAGE_EXECUTE_READWRITE,
    &oldProtect);
  QuickOverride();
  //---
  return !FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// Function :     QuickOverride
// Author :       Suslo P.
// License: free, not Gnu!
// Date :         16.12.2006
//-----------------------------------------------------------------------------
// Description : Overrides function. ProcessOverride must be executed once before.
///////////////////////////////////////////////////////////////////////////////
void SFnOverrideStruct::QuickOverride()
{
  BYTE* l_pAddr = (BYTE*)m_pFunctionPointerOld;
  *(WORD*)l_pAddr= OPCODE_RELATIVE_JMP;
  l_pAddr+=2;
  *(DWORD*)l_pAddr=(DWORD)&m_pFunctionPointerNew;
}

///////////////////////////////////////////////////////////////////////////////
// Function :     Restore
// Author :       Suslo P.
// License: free, not Gnu!
// Date :         16.12.2006
//-----------------------------------------------------------------------------
// Description : Restores function out of the override
///////////////////////////////////////////////////////////////////////////////
void SFnOverrideStruct::Restore()
{
  *(SSixBytes*)m_pFunctionPointerOld = m_sixbytesOldBytes;
}


-----------------------------
//test cpp
#include "stdafx.hpp"
#include "SFnOverrideStruct.hpp"

SFnOverrideStruct g_overrideStruct;

// test override of MessageBox
int WINAPI MessageBoxA_Override(HWND hWnd,
                               LPCSTR lpText,
                               LPCSTR lpCaption,
                               UINT uType)
{
  //--- preprocess
  Beep(300, 300);
  //--- call base
  g_overrideStruct.Restore();
  int l_nRetVal = MessageBoxA(hWnd,
                             lpText,
                             lpCaption,
                             uType);
  g_overrideStruct.QuickOverride();
  //--- postprocess
  Beep(400, 300);
  return l_nRetVal;
}

void main()
{
  //--- no beeps before override
  MessageBoxA(NULL, "Test", "Test_", MB_OK);
  //--- override
  g_overrideStruct.ProcessOverride(_T("user32.dll"),
    "MessageBoxA",
    (FARPROC)MessageBoxA_Override);
  //--- it is a call of an overrided function
  MessageBoxA(NULL, "Test2", "Test2_", MB_OK);
}
  
Наверх
 
IP записан
 
trad
1c++ power user
1c++ donor
1c++ moderator
Отсутствует



Сообщений: 3050
Местоположение: Киров
Зарегистрирован: 23. Мая 2006
Пол: Мужской
Re: Перехват функций
Ответ #1 - 16. Декабря 2006 :: 07:18
Печать  
в 1с++ уже есть CTrapSwap
  

1&&2&&3
Наверх
 
IP записан
 
artbear
1c++ developer
1c++ moderator
Отсутствует


Эх, дайте что-нибудь новенькое
да полезное потести

Сообщений: 6303
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Перехват функций
Ответ #2 - 16. Декабря 2006 :: 08:47
Печать  
Все давно уже придумано:

1) есть CDllMethodWrapper<TFunctionType> - шаблонный типизированный класс перехвата
а к нему CDllMethodWrapperGuard<TFunctionType> - шаблонный стековый объект для восстановления перехвата

2) в 1С++ есть CTrapSwap - нешаблонный нетипизированный класс перехвата,
к нему trapswaper - шаблонный стековый объект для восстановления перехвата

3) В Визуал1С++ я добавил к классам из п.2 шаблонный типизированный класс-надстройку
CTrap<TFunctionType> и CTrapGuard<TFunctionType> соответственно.
  

OpenConf developer :: http://openconf.1cpp.ru&&FormEx developer :: http://formex.dorex.ru&&1C++ active developer &amp;&amp; tester :: www.1cpp.ru
Наверх
GTalkSkype/VoIPICQ  
IP записан
 
artbear
1c++ developer
1c++ moderator
Отсутствует


Эх, дайте что-нибудь новенькое
да полезное потести

Сообщений: 6303
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Перехват функций
Ответ #3 - 16. Декабря 2006 :: 08:50
Печать  
Например, код перехваченной функции
Код
Выбрать все
int CWrapperForSyntaxCheck::Wrap_CBLModule_Compile() //int CBLModule7::Compile()
{
	CTrapGuard<TFunc_CBLModule7_Compile> trap(trCompile);

	CBLModule7* pMod = reinterpret_cast<CBLModule7*>(this);
	return CSyntax::ModuleCompile(pMod, bInsideCompileHandler);
}
 



И установка перехвата

Код
Выбрать все
	typedef int	(CBLModule7::*TFunc_CBLModule7_Compile)();
	static CTrap<TFunc_CBLModule7_Compile> trCompile;
	trCompile.SetTrapOnFunc((TFunc_CBLModule7_Compile)&CBLModule::Compile, &Wrap_CBLModule_Compile);
 



Все просто, удобно и компилятор сам следит за ошибками Улыбка
Т.е. чужую функцию не подсунешь !!
  

OpenConf developer :: http://openconf.1cpp.ru&&FormEx developer :: http://formex.dorex.ru&&1C++ active developer &amp;&amp; tester :: www.1cpp.ru
Наверх
GTalkSkype/VoIPICQ  
IP записан
 
artbear
1c++ developer
1c++ moderator
Отсутствует


Эх, дайте что-нибудь новенькое
да полезное потести

Сообщений: 6303
Местоположение: Москва
Зарегистрирован: 19. Мая 2006
Пол: Мужской
Re: Перехват функций
Ответ #4 - 16. Декабря 2006 :: 08:53
Печать  
Наверное, ты изучал очень-очень старые исходники 1С++ Печаль Улыбка
  

OpenConf developer :: http://openconf.1cpp.ru&&FormEx developer :: http://formex.dorex.ru&&1C++ active developer &amp;&amp; tester :: www.1cpp.ru
Наверх
GTalkSkype/VoIPICQ  
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #5 - 16. Декабря 2006 :: 11:08
Печать  
PavelS

Самое важное - это обеспечить корректное каскадирование, когда один и тот же перехват выполняют множество модулей.
CTrapSwap в этом плане - самое то, больше ничего не нужно.
И ключевой момент - это момент снятия перехвата (если уж он снимается) без нарушения каскада.

P.S.
И, кстати, 6 байт - это не модно, лучше 5  Подмигивание
  

De quelle planète es-tu?
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #6 - 16. Декабря 2006 :: 11:25
Печать  
Ну и собственно, 2 вопроса напоследок:

PavelS:
Твой вариант позволит ли держать рабочий каскад?

artbear:

Объясни мне, пожалуйста смысл классов
Цитата:
CDllMethodWrapperGuard и CTrapGuard


как их эффективно использовать?
  

De quelle planète es-tu?
Наверх
 
IP записан
 
PavelS
Junior Member
**
Отсутствует


I Love YaBB 2!

Сообщений: 16
Зарегистрирован: 14. Декабря 2006
Re: Перехват функций
Ответ #7 - 16. Декабря 2006 :: 17:05
Печать  
artbear писал(а) 16. Декабря 2006 :: 08:53:
Наверное, ты изучал очень-очень старые исходники 1С++ Печаль Улыбка
Не знаю, 1cpp-2.0.3.1.src - это старые? Смотрел на http://www.1cpp.ru/.
Searching for 'for(i = 0; i<6;i++)'...
\1cpp-2.0.3.1.src\Source\MethodsWrapper.h(149):            for(i = 0; i<6;i++) origWrapFindFunc[i] = *adr1++;
....
79 occurrence(s) have been found.
Таки такая байда повсеместно применяется. А зачем? Я понимаю, что "эта хреновина работает - не трогай её", но таки что бы не причесать код? Дышать легче будет.
  
Наверх
 
IP записан
 
PavelS
Junior Member
**
Отсутствует


I Love YaBB 2!

Сообщений: 16
Зарегистрирован: 14. Декабря 2006
Re: Перехват функций
Ответ #8 - 16. Декабря 2006 :: 17:11
Печать  
kms писал(а) 16. Декабря 2006 :: 11:08:
И, кстати, 6 байт - это не модно, лучше 5  Подмигивание
Мне говорили что модно играть не с байтиками функций, а править указатели в таблицах импорта-экспорта, тогда многопоточная работа будет как надо (хотя её и нет).

Касательно снятия перехватов в каскаде, то не представляю как можно снять перехват иначе как в порядке, обратном установке - если перехваты ставят сразу несколько модулей без общих переменных, т.е. из разных Dll.
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #9 - 16. Декабря 2006 :: 17:19
Печать  
PavelS писал(а) 16. Декабря 2006 :: 17:11:
Касательно снятия перехватов в каскаде, то не представляю как можно снять перехват иначе как в порядке, обратном установке - если перехваты ставят сразу несколько модулей без общих переменных, т.е. из разных Dll.

Ясно, вопросов больше не имею.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #10 - 16. Декабря 2006 :: 17:30
Печать  
PavelS писал(а) 16. Декабря 2006 :: 17:05:
Таки такая байда повсеместно применяется. А зачем? Я понимаю, что "эта хреновина работает - не трогай её", но таки что бы не причесать код? Дышать легче будет.

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

Ах, да, еще одна важная нерешенная тема: почему 1cpp не компилируется под msvc 8.0?
Серьезная недоработка!

PavelS
Твоя реализация даже поставить каскад нормально не может. Может, не стоит с таким подходом искать соринки в чужих глазах? Нерешительный
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Uzhast
1c++ power user
Отсутствует



Сообщений: 1341
Зарегистрирован: 30. Августа 2006
Пол: Мужской
Re: Перехват функций
Ответ #11 - 16. Декабря 2006 :: 17:57
Печать  
А CTrapSwap разве обеспечит корректный перехват/снятие в случае нескольких разных ВК, которые периодически ставят/снимают перехват? Например, два перехвата от двух ВК.

1-я ВК снимает перехват и восстанавливает исходный начальный код функции. В результате перехват идет лесом и для второй ВК.

Ситуацию может ухудшить снятие перехвата дополнительно со стороны 2-й ВК. 2-я ВК снимает перехват и восстанавливает исходный код функции (где стоит джамп на функцию 1-й ВК). Получается, начинает работать отключенный перехват в 1-й ВК.

Или это все корректно отрабатывается указанным классом? Если вопрос ламерский, извините Улыбка
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #12 - 16. Декабря 2006 :: 18:13
Печать  
Дело в том, что этот простой вопрос - это лакмусовая бумажка.
И показательна вовсе не квалификация, а подход к решению.

По каскаду:
CTrapSwap поддерживает возможность снятия произвольного звена каскада, в отличие от вышеприведенного шедевра.
Реально важен момент снятия перехвата: он определен и не является произвольным.

Я утверждаю, что CTrapSwap + снятие перехвата внутри обработчика решают вопрос обеспечения корректного каскадирования.

Цитата:
Или это все корректно отрабатывается указанным классом? Если вопрос ламерский, извините Улыбка

Есть перцы, которых хватает на то, чтобы подсчитать число вхождений "for (i=0; i<6)", но заглянуть в код и обнаружить, что 99% вхождений находятся в комментариях, они не в состоянии.
С такими мне просто западло терять время.

Есть люди, которые думают о народе.
Таким людям приятно ответить и с ними хотелось бы работать.
Красиво я сказал?  Улыбка
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Uzhast
1c++ power user
Отсутствует



Сообщений: 1341
Зарегистрирован: 30. Августа 2006
Пол: Мужской
Re: Перехват функций
Ответ #13 - 16. Декабря 2006 :: 18:18
Печать  
Может быть, стоит подумать о реализации интерфейса-делегата для перехвата функций 1С? Видится следующим образом:

1) Перехват делается один раз специальным классом.

2) Далее ВК для перехвата обращается к этому интерфейсу. Интерфейс хранит список адресов, которые должны вызываться при вызове исходной функции 1С.

3) Снятие перехвата происходит также обращением к этому интерфейсу. При этом просто происходит удаление адреса из списка. Если количество адресов стало равным нулю, то можно даже вообще снять перехват с функции.

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

Взаимодействие нескольких ВК

При загрузке ВК определяет, существует ли уже в системе интерфейс-делегат. Если не существует, то создает и прописывает. Для хранения ссылки на интерфейс нужно что-то придумать. Например, можно попробовать механизм свойств 1С (CPropBox?) или можно подцепить какое-нибудь свойство к главному окну 1С  Круглые глаза
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Перехват функций
Ответ #14 - 16. Декабря 2006 :: 18:29
Печать  
Здравая мысль!
.
Правда, не очень новая  Подмигивание

Давай я не буду сочинять заново, воспользуюсь буфером обмена:
Цитата:
ОК.

Давайте по архитектуре перехватов примем решение.

Сейчас озвучено 2 варианта:
1. Корректное (стандартизованное) каскадирование
2. Глобальный сервис по управлению перехватом и подпиской

Собственно, это просто 2 различных архитектуры - как сетевые топологии token ring и звезда.

P.S.
Вот тезисы безопасности каскадирования из поста №1:

Момент безопасного удаления ИМХО находится внутри обработчика перехвата, безопасный метод замены джампов - swap (т.е. метод учитывает, что после возвращения из вызываемого каскада, содержимое по адресу перехвата может измениться).

P.P.S.
Можно и не бежать впереди паровоза и не принимать стандартов - просто решить текущие вопросы, и все.
Но как любой архитектурный вопрос, он будет возникать снова и снова, пока не будет решен.

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


Это была тема о стандартах перехватов.
Действительно, когда припрет, придется принимать решение о стандарте.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: [1] 2 
ОтправитьПечать