C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Работа с указателями на методы
Deem
Отправлено: 28.04.2005, 17:44


Мастер участка

Группа: Участник
Сообщений: 327



Я как-то получил-таки такой указатель и преобразовывал его к void* для дальнейшей работы. Сйчас надо, а нет того файла.
Кто подскажет, кто знает?
Если просто функция или метод статик — так нет проблем.
А если просто метод ? Помню, тогда я порадовался за борланд, т.к. получилось благодаря какому-то его расширению языка. Может это был модификатор _closure (?), но с таким указателем потом ничего не слепишь: только присваивать или запускать функцию через него.
Есть в си штука: указывать класс, к которому принадлежит метод
TForm1::MyMethod, но указатель получается неконвертируемый какой-то.
А надо бы нормальный доступ в память.
А может я опять что-то перепутал smile.gif
Rius
Отправлено: 28.04.2005, 18:58


Мастер участка

Группа: Участник
Сообщений: 321



тип функции
typedef HANDLE (__cdecl *tdefCloseDevice) (HANDLE fHandle);

указатель на функцию
tdefCloseDevice dCloseDevice;

получение указателя из DLL
dCloseDevice = (tdefCloseDevice) GetProcAddress(DLL.Handle, "_dCloseDevice");

обращение к функции
HANDLE h = dCloseDevice(Handle);

метод есть функция без результата, сработать должно аналогично

Отредактировано Rius — 28/04/2005, 21:59
xim
Отправлено: 28.04.2005, 20:47


Станционный диспетчер

Группа: Участник
Сообщений: 143



Указатель на метод ничего не даст (если он не static конечно) без соответствующего экземпляра класса. Первым параметром при объявлении метода всегда является указатель на представитель класса. Понятно почему таким образом можно получить указатель на static метод — вместо 1 параметра можно указать NULL. Для published методов можно сделать так:
CODE

#include <vcl.h>

class TMyClass : public TObject{
  public:
     __fastcall TMyClass();
     virtual __fastcall ~TMyClass();
  __published:
     void method1();
};
__fastcall TMyClass::TMyClass()
{
}
__fastcall TMyClass::~TMyClass()
{
}
void TMyClass::method1()
{
  ShowMessage("method1");
}

typedef void (*PMethod1)(TMyClass *);

int main()
{
  TMyClass *cl=new TMyClass();
  PMethod1 meth=(PMethod1)TMyClass::MethodAddress(__classid(TMyClass),"method1");
  if(meth)meth(cl);
  delete cl;
  return 0;
}
olegenty
Отправлено: 29.04.2005, 07:01


Ветеран

Группа: Модератор
Сообщений: 2412



ну а с closure я работаю так (собственно, это интерфейсная часть exe, с которой работают все dll):

Callback.h
CODE

#ifndef CCallbackH
#define CCallbackH

#include <Forms.hpp>
#include <ADODB.hpp>

enum TUserRole {urRead, urWrite, urCheck};

typedef TForm*          __stdcall (__closure *PCommon  )(const AnsiString);
typedef TADOConnection* __stdcall (__closure *PConn    )();
typedef HWND            __stdcall (__closure *PFMHandle)();
typedef TDateTime       __stdcall (__closure *PDate    )();
typedef TUserRole       __stdcall (__closure *PUserRole)(const HWND);
typedef void            __stdcall (__closure *PShowWait)();
typedef void            __stdcall (__closure *PHideWait)();

class CCallback
{
private:
   PCommon   m_pCommon ;
   PConn     m_pConn   ;
   PFMHandle m_pHandle ;
   PDate     m_pDate   ;
   PUserRole m_pUserRole;
   PShowWait m_pShowWait;
   PHideWait m_pHideWait;
public:
   __fastcall CCallback(PCommon   Common  ,
                        PConn     Conn    ,
                        PFMHandle FMHandle,
                        PDate     Date    ,
                        PUserRole UserRole,
                        PShowWait ShowWait,
                        PHideWait HideWait);

   TForm*          __stdcall Common(const AnsiString Name);
   TADOConnection* __stdcall Conn();
   HWND            __stdcall Handle();
   TDateTime       __stdcall Date();
   TUserRole       __stdcall UserRole(const HWND Handle);
   void            __stdcall ShowWait();
   void            __stdcall HideWait();
};

#endif

Callback.cpp
CODE

#pragma hdrstop

#include "CCallback.h"

#pragma package(smart_init)

__fastcall CCallback::CCallback(PCommon   Common  ,
                               PConn     Conn    ,
                               PFMHandle FMHandle,
                               PDate     Date    ,
                               PUserRole UserRole,
                               PShowWait ShowWait,
                               PHideWait HideWait)
{
   m_pCommon   = Common ;
   m_pConn     = Conn   ;
   m_pHandle   = FMHandle;
   m_pDate     = Date   ;
   m_pUserRole = UserRole;
   m_pShowWait = ShowWait;
   m_pHideWait = HideWait;
}

TForm* __stdcall CCallback::Common(const AnsiString Name)
{
   return m_pCommon(Name);
}

TADOConnection* __stdcall CCallback::Conn()
{
   return m_pConn();
}

HWND __stdcall CCallback::Handle()
{
   return m_pHandle();
}

TDateTime __stdcall CCallback::Date()
{
   return m_pDate();
}

TUserRole __stdcall CCallback::UserRole(const HWND Handle)
{
   return m_pUserRole(Handle);
}

void __stdcall CCallback::ShowWait()
{
   m_pShowWait();
}

void __stdcall CCallback::HideWait()
{
   m_pHideWait();
}


ну а в главной форме экземпляр класса CCallback создаётся для последующей передачи в DLL (и инициируется указателями на методы главной формы) так:
CODE

Callback = new CCallback(&Common,
                            &Conn,
                            &FormHandle,
                            &Date,
                            &UserRole,
                            &ShowWait,
                            &HideWait);
AVC
Отправлено: 29.04.2005, 08:57


Ветеран

Группа: Модератор
Сообщений: 1583



2xim
QUOTE

Указатель на метод ничего не даст (если он не static конечно) без соответствующего экземпляра класса.

Даст, если он хочет его шифровать smile.gif (мне алгоритм очень не нравится)
А еще его можно "грамотно" применять. Пример:
CODE

Как добраться до protected свойства.
class a {protected int prot;}
Задача добраться до a:prot
делаем описание (можно прямо перед функцией)
class b : public a
{private privb;
public int GetProta (void);
};
b::GetProt(int) { return a::prot; }; // у меня обязательно не inline
Использование
.....
b *bpoi = (b*)(указатель на экземпляр класса a);
prot = bpoi->GetProta();

Обратите внимание, что экземпляр класса b не создается и, что естественно, bpoi->privb содержит "мусор" если его можно так назвать. А вот метод вернет реальное значение.


Преобразования методов vcl к нативным функциям и использование
CODE

//описание указателей на методы
typedef void __fastcall (__closure *TAxTimerCallBack_vcl)(TTinyOnePrompt*);
typedef void __fastcall ( *TAxTimerCallBack_nat)(void*, TTinyOnePrompt*);
// постфих _vcl — метод класса, _nat "просто" функция

// использование
// получаю как метод объекта _vcl сохраняю как указатели
int __fastcall TDM::TimerQue_Add
    (TDateTime pWhen
    ,TAxTimerCallBack_vcl cbfun
    ,TForm  *pForm
   )
{
...
TMethod Method = *(TMethod*)&cbfun;
TTinyOnePrompt *tp = FTimer_Queue->Add(tque_code, name);
tp->Code1 = int(Method.Code);
tp->Code2 = int(pForm);
...
}

// вызов _nat
void __fastcall TDM::TimerQue_Dispatch (void)  // Выполнить все наступившие
{
TTinyPrompt *plist = FTimer_Queue;
TTinyOnePrompt *itm;
TAxTimerCallBack_nat fun;
TForm *frm;
...
for (int i=0; i < plist->Count; i++)
{  itm = plist->Items[i];
   ....
   fun  = NULL;
   frm  = NULL;
   if (itm->Code1 != 0) fun = TAxTimerCallBack_nat (itm->Code1);
   if (itm->Code2 != 0) frm = (TForm*) (itm->Code2);

   if (!fun) continue;
   ....
   if (frm) fun(frm, itm);
   else       fun(Application, itm);
Deem
Отправлено: 29.04.2005, 13:09


Мастер участка

Группа: Участник
Сообщений: 327



Ойой! Закидали. smile.gif Однако, про прямой доступ к "телу" метода (если не пропустил ничего) у вас нету. Получить указатель для последующего запуска через него можно от чего угодно.
А метод, даже если он не статик, гже-то же валяется и память занимает. Надо было до этой самой памяти доступ получить. DLL ваще не рассматриваю. Все в эхешнике одном.
Ну ладно, видимо нет такого варианта. А указатели, получаемые при помощи указания экземпляра класса (а может и __closure, не смотрел) занимают аж 12 байт! Понятно, что к void* никто не захочет приводить. smile.gif
А MethodAddress, как я понял, только с __pablushed (?) — методами работает.
Георгий
Отправлено: 29.04.2005, 20:52


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



у борланда _closure, но это не стандартно.
а есть ещё библиотека http://www.boost.org/ с неплохими классами для работы с указателями на методы (см boost::bind) причём работает она с ANSI C++ без всяких нестандартных расширений

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

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

можно ещё по указателю на метод reinterpret_cast проехаться — он то точно к void* сможет преобразовать, но что из этого выйдет даже не догадываюсь

PS. если это всё затеяно, что бы защититься от отладчиково вроде SoftIce, то ничего не выйдет — он работает в 1 или 0 кольце защиты, а ты в 3м. Обманет он тебя — сам сможет писать в твой сегмент кода, а тебе не позволит. лучше купить защиту вроде ключей alladin — их разработчики как обещают защиту от крякеров.

Отредактировано Георгий — 29/04/2005, 23:55
Asher
Отправлено: 04.05.2005, 08:12


Мастер участка

Группа: Модератор
Сообщений: 550



Привет.
QUOTE
вторые 4 = индекс в таблице. значения отличный от 0 не видел
третьи 4 = смещение. так же значений отличных от нуля не видел.

А при множественном наследовании смотрел? biggrin.gif
Георгий
Отправлено: 04.05.2005, 08:28


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



QUOTE (Asher @ 04/05/2005, 08:12)
А при множественном наследовании смотрел? biggrin.gif

на брилиантике смотрел:
CODE
class root{virtual void method();};
class child1:public root{virtual void method();};
class child2:public root{virtual void method();};
class multi:public child1, public child2{virtual void method();}
всё равно нули. только поле адрес метода имеет другое значение и функция заглушка передеёт управление по этому значение, проводя какие то непонятные вычисление с использованием других полей (хоть они и нулевые, но всё равно что-то с их использованием вычисляется).
а вот явный вызов метода
CODE
child1::method();
эти дополнительный поля вообще не используются.

PS. что самое интересное код генерируемый Wactom C10.6 (1996 год выпуска) и BCB6 (куда более свежий компилятор) практически совпадает при работе с виртуальными методами biggrin.gif
Asher
Отправлено: 04.05.2005, 09:10


Мастер участка

Группа: Модератор
Сообщений: 550



По идее объект полученный в результате множественного наследования от полиморфных классов будет содержать несколько “унаследованных” указателей на vtbl.
Может у тебя виртуального деструктора нехватает, и поэтому компилятор, прикинув невозможность нормального преобразования указателя на производный класс в указатель на любой из базовых, отоптимизировал таблицу vtlb.

Я в вопросе плотно не разбирался, поэтому все это чисто домыслы и умозрения.
Deem
Отправлено: 04.05.2005, 09:30


Мастер участка

Группа: Участник
Сообщений: 327



Посмотрю библиотечку. А с дебугерами все просто: ты сам можешь писать в тот блок памяти, в котором сейчас стоит брякпоинт дебугера. smile.gif А в других запись невозможна (если сам не заказал доступ). Вот так-то. Короче, можно так сделать: при входе в каждую функцию пробовать писать в себя же: если можно — тута где-то валяется бркпоинт, и дебугер шастает поблизости, соответственно. biggrin.gif
Не знаю, как это хлопотно, но догадываюсь.
Сам так не собираюсь делать, хотя, кто знает. А самомодифицирующийся код — пока что самый крутой и действенный метод зпщиты. Как минимум, дизассемблер ехешник просто так не расковыряет. Пока что я использовал VirtualProtect, что меня смущает, т.к. именно его (я думаю) можно отследить. А если отследить, значит и поковырять все остальное. Хотя можно ставить разрешение на запись для очень большого куска кода, а не коткретной функции. Тогда хакер долго будет искать, что модифицировалось.
Ну пока я только обощаю теорию, первые шаги. После них будут вторые и третьи. А брать готовую не советую никому. Крякеры уже знают и ковыряют ее.
А хардкей тоже не поможет: ломается, да и хлопотно с ним.
Защита должна быть у каждого своя.
А ваще БГ сволочь: защита должна реализовываться на уровне операционки. Правда, он и сам не в силах защититься. smile.gif

Вернуться в Вопросы программирования в C++Builder