Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.04.20;
Скачать: CL | DM;

Вниз

Определение модуля, вызвавшего функцию   Найти похожие ветки 

 
Пробегал...   (2008-03-06 20:21) [0]

Есть программа и система плагинов к ней, каждый плагин является DLL. Чтобы плагин умел работать с ядром - он экспортирует некоторые функции, которые вызывает ядро и передает ему информацию и ссылки на функции ядра, которые можно вызыать. Это уже работает так.

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

function ExecuteCommand(SrcPlugin: THandle; command: integer): integer; stdcall;

плагин соответственно сам передает SrcHandle каждый раз при вызове функций ядра. Это собственно то, что само ядро ему и сообщило, а в итоге результат вызова LoadLibrary, то есть ссылка на начало образа DLL в ВАП, как я понимаю.

И вот интересно, а можно обойтись без этого? То есть, плагин просто вызывает:

function ExecuteCommand(command: integer): integer; stdcall;

а функция сама определяет по адресу возврата этот самый THandle?

Только нужен абсолютно надежный способ, с которым невозможны косяки. Какие это подводные камни несет? И как это сделать?


 
VirEx ©   (2008-03-06 20:34) [1]

абсолютно надежный:
record
ресурс
константа


 
Loginov Dmitry ©   (2008-03-06 20:59) [2]

> Какие это подводные камни несет?


Это усложняет код. Делает его непонятным. Совершенно необоснованно.


> И как это сделать?


Видимо, сравнить адрес возврата, хранимый в стэке, с результатом LoadLibrary().


 
Пробегал...   (2008-03-06 22:16) [3]

VirEx ©   (06.03.08 20:34) [1]
абсолютно надежный:
record
ресурс
константа


ничего не понял

Loginov Dmitry ©   (06.03.08 20:59) [2]
Совершенно необоснованно


ну почему не обосновано:

1) облегчает жизнь плагину, не надо каждый раз передвать и вообще помнить свой instance... Хотя конечно его и легко узнать, но тем не менее.

Например, в miranda сервисные функции не запрашивают чтобы плагины передавали свой описатель при вызове. Хотя как-то он же их идентифицирует? А может конечно и нет...

2) позволяет сделать защиту, чтобы один плагин не выдал себя за другой ;) Хотя это я понимаю притянуто за уши, если плагин захочет напакостить - то уж в ВАП процесса он может наворотить что угодно.


 
Пробегал...   (2008-03-06 22:21) [4]

Loginov Dmitry ©   (06.03.08 20:59) [2]
Видимо, сравнить адрес возврата, хранимый в стэке, с результатом LoadLibrary().


Это ты к чему написал? Даже я понимаю, что такое сравнение никогда не пройдет, LoadLibrary возвращает ссылку на начало загруженного образа, а адрес возврата содержит ссылку на адрес кода, который должен начать исполняться после этой функции.


 
Loginov Dmitry ©   (2008-03-06 22:45) [5]

> облегчает жизнь плагину, не надо каждый раз передвать и
> вообще помнить свой instance


а зачем его помнить? Он запоминается в hInstance.


> позволяет сделать защиту, чтобы один плагин не выдал себя
> за другой


если ты уже загрузил плагин, то что защитить хочешь? Конец света! Опоздал! ;)


> Даже я понимаю, что такое сравнение никогда не пройдет,
> LoadLibrary возвращает ссылку на начало загруженного образа,
> а адрес возврата содержит ссылку на адрес кода


Почему не пройдет? У тебя есть адрес расположения библиотеки в памяти, возвращаемый LoadLibrary. Каким-то образом определяешь, где оканчивается область кода данной библиотеки. Извлекаешь из стека адрес возврата и проверяешь, находится ли он в данном диапазоне. По-моему так. Может я не прав. Такой хренью ни разу не загонялся.


 
Пробегал...   (2008-03-07 00:22) [6]

Loginov Dmitry ©   (06.03.08 22:45) [5]
а зачем его помнить? Он запоминается в hInstance


откуда я знаю на чем там будут плагины писать потенциально сторонние разработчики.


 
Пробегал...   (2008-03-07 00:25) [7]

Loginov Dmitry ©   (06.03.08 22:45) [5]
Каким-то образом определяешь, где оканчивается область кода данной библиотеки. Извлекаешь из стека адрес возврата и проверяешь, находится ли он в данном диапазоне. По-моему так. Может я не прав. Такой хренью ни разу не загонялся.


хех. Да по-моему все проще ;) Определить образ по указателю - по-моему, есть для этого такая специальная WinApi функция. Ей передаешь указатель на область кода, а она возвращает описатель модуля, загруженного по этому адресу ;)

Вопрос сводится к:

1) как называется эта функция

2) как определить адрес возврата, видимо во вставке на ассемблере, а я в этом не силен

3) какие могут быть подводные камни при таком подходе автоматического определения.


 
Loginov Dmitry ©   (2008-03-07 00:27) [8]

> откуда я знаю на чем там будут плагины писать потенциально
> сторонние разработчики.


ну тогда это сугубо их проблемы, как хранить (как определить, и нужно ли это вообще) hInstance.


 
Пробегал...   (2008-03-07 00:54) [9]

Loginov Dmitry ©   (07.03.08 0:27) [8]
сугубо их проблемы, как хранить (как определить, и нужно ли это вообще) hInstance


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


 
Slym ©   (2008-03-07 05:00) [10]

для таких случаев родная Дельфя пользует Sender:TSomeObg, иначе получается игра "Кто стукнул в спину"


 
Rouse_ ©   (2008-03-07 10:42) [11]


> а функция сама определяет по адресу возврата этот самый
> THandle?

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

Ну к примеру вот так:
Библиотека:

library plugin;

{$R *.res}

uses
 Windows,
 SysUtils;

type
 TExecuteCommand = function(command: integer): integer; stdcall;

procedure TestPlugin(lpParams: Pointer); stdcall;
var
 ExecuteCommand: TExecuteCommand;
 I: Integer;
 SleepTime: DWORD;
begin
 Randomize;
 @ExecuteCommand := GetProcAddress(GetModuleHandle(nil), "ExecuteCommand");
 if not Assigned(@ExecuteCommand) then
   raise Exception.Create("Ошибка определения адреса функции ядра.");
 for I := 0 to 9 do
 begin
   SleepTime := Random(4) * 1000;
   Sleep(SleepTime);
   ExecuteCommand(I);
 end;
end;

var
 Dumme: THandle;
begin
 CreateThread(nil, 0, @TestPlugin, nil, 0, Dumme);
end.


Приложение:

unit uEngine;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls;

type
 TPluginInfo = record
   PluginNage: String;
   PluginHandle: THandle;
   PluginSize: Int64;
 end;

 TEngineDlg = class(TForm)
   memLog: TMemo;
   procedure FormCreate(Sender: TObject);
   procedure FormClose(Sender: TObject; var Action: TCloseAction);
 public
   Plugins: array of TPluginInfo;
   procedure LoadPlugins;
   procedure ReleasePlugins;
   procedure Log(const Value: String);
 end;

var
 EngineDlg: TEngineDlg;
 RTL: TRTLCriticalSection;

implementation

{$R *.dfm}

function ExecuteCommand(command: integer): integer; stdcall;
var
 CallAddr: DWORD;
 I: Integer;
 Founded: Boolean;
begin
 EnterCriticalSection(RTL);
 try
   asm
     mov eax, [esp + $4C];
     mov CallAddr, eax
   end;
   Founded := False;
   for I := 0 to Length(EngineDlg.Plugins) - 1 do
     if (CallAddr > EngineDlg.Plugins[I].PluginHandle) and
       (CallAddr < EngineDlg.Plugins[I].PluginHandle +
       EngineDlg.Plugins[I].PluginSize) then
     begin
       Founded := True;
       EngineDlg.Log(Format(
         "Был произведен вызов функции ядра из библиотеки %s, параметр %d",
         [EngineDlg.Plugins[I].PluginNage, command]));
       Break;
     end;
   if not Founded then
     EngineDlg.Log(Format(
       "Был произведен вызов функции ядра из неизвестной библиотеки," +
       " адрес возврата %x, параметр %d",
       [CallAddr, command]));
 finally
   LeaveCriticalSection(RTL);
 end;
end;

exports
 ExecuteCommand;

{ TEngineDlg }

procedure TEngineDlg.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 ReleasePlugins;  
end;

procedure TEngineDlg.FormCreate(Sender: TObject);
begin
 LoadPlugins;
end;

procedure TEngineDlg.LoadPlugins;
var
 SR: TSearchRec;
 Len: Integer;
 Path: String;
begin
 Len := 0;
 Path := ExtractFilePath(ParamStr(0));
 if FindFirst(Path + "*.dll", faAnyFile, SR) = 0 then
 try
   repeat
     Inc(Len);
     SetLength(Plugins, Len);
     Plugins[Len - 1].PluginNage := SR.Name;
     Plugins[Len - 1].PluginHandle := LoadLibrary(PChar(Path + SR.Name));
     Plugins[Len - 1].PluginSize := SR.Size;
     Log("Загружена библиотека: " + SR.Name);
   until FindNext(SR) <> 0;
 finally
   FindClose(SR);
 end;
end;

procedure TEngineDlg.Log(const Value: String);
begin
 memLog.Lines.Add(TimeToStr(Now) + ": " + Value);
end;

procedure TEngineDlg.ReleasePlugins;
var
 I: Integer;
begin
 for I := 0 to Length(Plugins) - 1 do
   FreeLibrary(Plugins[I].PluginHandle);
end;

initialization

 InitializeCriticalSection(RTL);

finalization

 DeleteCriticalSection(RTL);

end.


 
Игорь Шевченко ©   (2008-03-07 11:07) [12]

Кулибины


 
www   (2008-03-07 11:47) [13]


> в miranda сервисные функции не запрашивают чтобы плагины
> передавали свой описатель при вызове. Хотя как-то он же
> их идентифицирует?

исходники есть, смотри


 
Сергей М. ©   (2008-03-07 11:56) [14]


> 1) как называется эта функция


FindHInstance


> 2) как определить адрес возврата


Он нафих не нужен.

procedure Dummy;
begin
end;

..

MyResult := ExecuteCommand(FindHInstance(@Dummy); SomeCommand);


 
Пробегал2...   (2008-03-07 12:43) [15]

Rouse_ ©   (07.03.08 10:42) [11]

ну идея понятна. Начало образа в ВАП мы знаем, прибавляем к нему размер библиотеки - и получаем конец... А это точно справедливо? Если на диске библиотека занимает 50 тысяч байт, она в памяти процесса тоже будет занимать ровно 50 тысяч байт?

Но видишь кстати есть способ проще мне кажется: FindHInstance

Игорь Шевченко ©   (07.03.08 11:07) [12]
Кулибины


типа "вы все делаете неправильно, а я знаю как правильно, но не скажу". К чему такие посты, Игорь? А потом обижаетесь, что вам хамят.

Сергей М. ©   (07.03.08 11:56) [14]
FindHInstance


да, точно... Спасибо!

Сергей М. ©   (07.03.08 11:56) [14]
MyResult := ExecuteCommand(FindHInstance(@Dummy); SomeCommand);


а что такое ExecuteCommand?


 
Пробегал2...   (2008-03-07 12:46) [16]

а-а-а... Это ты взял функцию из поста Rouse_...

Ну теперь все ясно. Функция называется FindHInstance, сам код получения адреса возврата я так понимаю:

asm
    mov eax, [esp + $4C];
    mov CallAddr, eax
  end;


Это для функции с одним параметром? А если функция с двумя параметрами или тремя?

И осталось выяснить - несет ли это в себе какие подводные камни?


 
Сергей М. ©   (2008-03-07 12:47) [17]


> что такое ExecuteCommand?


Как это что ?

Та самая "функция ядра", о которой ты завел речь в самом первом своем посте)


 
Сергей М. ©   (2008-03-07 12:49) [18]


> Пробегал2...   (07.03.08 12:46) [16]
>
> а-а-а... Это ты взял функцию из поста Rouse


Пробегай помедленее, мож и в своем посте увидишь)


 
Игорь Шевченко ©   (2008-03-07 12:59) [19]

Пробегал2...   (07.03.08 12:43) [15]

Когда коту нечего делать, он делает известно что. Мне непонятно желание странного - идентификация чего-либо через косвенные анальные отверстия, вместо явной идентификации.


 
Пробегал2...   (2008-03-07 13:05) [20]

Сергей М. ©   (07.03.08 12:47) [17]
Как это что ?

Та самая "функция ядра", о которой ты завел речь в самом первом своем посте)


а... Теперь понял, что ты не понял ;)
А откуда ты тогда взял адрес функции Dummy? Да и что это за функция?

Есть ядро. Есть плагин в виде DLL. Ядро загружает плагин функцией LoadLibrary и передает ему адрес некоторых системных функций, с которым плагин и работает.

Плагин из любого места своего кода может вызвать эти функции. Они вызовутся, а дальше что? Откуда ядро знает КТО вызвал эту функцию? Ведь адрес этой функции известен многим плагинам. Поэтому адрес возврата нужено определять.


 
Пробегал2...   (2008-03-07 13:06) [21]

Игорь Шевченко ©   (07.03.08 12:59) [19]
Мне непонятно желание странного - идентификация чего-либо через косвенные анальные отверстия


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


 
oxffff ©   (2008-03-07 13:10) [22]

GetModuleHandle и GetModuleInformation?


 
oxffff ©   (2008-03-07 13:14) [23]

mov eax,[ebp+04]; Адрес возврата при наличии Stack frames


 
oxffff ©   (2008-03-07 13:15) [24]

function ExecuteCommand(command: integer): integer; stdcall;
begin
mov eax,[ebp+04];
Далее FindHinstance предложенный Сергей М. либо [22]

. И все!!!!!!!! :))))))))))
end;


 
Сергей М. ©   (2008-03-07 13:17) [25]


> Пробегал2...   (07.03.08 13:05) [20]


> Теперь понял, что ты не понял


Я-то как раз понял)


> откуда ты тогда взял адрес функции Dummy?


От барана)

"Барана" перед Dummy видишь ? Вот отттуда и взял.


> и что это за функция?


По барабану что за функция.
В кач-ве Dummy можешь взять любую (в области видимости) регулярную процедуру или функцию, объявленную где-либо в проекте плагина.


> Откуда ядро знает КТО вызвал эту функцию?


Если под "КТО" подразумевается уникальное значение hInstance плагина, то плагин сам определит его определит (см. мой пример) и передаст в кач-ве первого параметра (см. твой первый пост).

Нафих при этом тебе сдались эти "адреса возврата" - ума не приложу. Потому, imho, в [19] совершенно правильная критика.


 
Сергей М. ©   (2008-03-07 13:24) [26]


> почему разработчики Miranda (люди не дураки уверен) сделали
> по-другому


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


 
Игорь Шевченко ©   (2008-03-07 13:32) [27]


> function ExecuteCommand(command: integer): integer; stdcall;
>
> begin
> mov eax,[ebp+04];


Все бы хорошо, но есть гарантия, что у функции ВСЕГДА будет сгенерирован стандартный пролог ?


 
oxffff ©   (2008-03-07 14:24) [28]


> Игорь Шевченко ©   (07.03.08 13:32) [27]


Я же написал, что для этого трюка необходима генерация стекового фрейма.
А если вы посмотрите генерируется всегда если есть например managed переменные. И функция сервера не пустая.
Так что мой трюк должен работать.


 
VirEx ©   (2008-03-07 18:30) [29]


>  [3] Пробегал...   (06.03.08 22:16)
> VirEx ©   (06.03.08 20:34) [1]
> абсолютно надежный:
> record
> ресурс
> константа
>
> ничего не понял

А чего не понятного?
1. record
type
 TInitPlag=record
  Name:Pchar;
  Version:integer;
 end;

var
 p:TInitPlug;
begin
h:=LoadLibrary();
foo:=GetProcAddress(h,"InitPlugin");
if Assigned(foo) then
p.Name:=StrAlloc(1024);
foo(p);
в "p" теперь информация о плагине
else это не плагин вобще

В длл
InitPlugin(var PlugInfo:TInitPlug);
begin
 PlugInfo.Name:=
 PlugInfo.Version:=
Есть минусы, например и плагин и ядро должны иметь одинаковый TInitPlug

2. ресурс с именем и версией плагина запихиваем в длл, в ядре загружаем данные ресурсы

3. в длл делаем константу (или в асме как-то делается) с именем и версией, ядро ищет эту константу как строку.
но это метод экстремалов :)


 
VirEx ©   (2008-03-07 18:40) [30]


> [29] VirEx ©   (07.03.08 18:30)

begin end забыл



Страницы: 1 вся ветка

Текущий архив: 2008.04.20;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.022 c
15-1204861461
Slider007
2008-03-07 06:44
2008.04.20
С днем рождения ! 7 марта 2008 пятница


2-1206121928
DJ_UZer
2008-03-21 20:52
2008.04.20
Выделение в мемо


3-1195737356
dik
2007-11-22 16:15
2008.04.20
Пустые блобы


2-1206529994
saNat
2008-03-26 14:13
2008.04.20
Ошибка при создании записи, одно из полей которой пусто.


2-1206517260
проня
2008-03-26 10:41
2008.04.20
Помогите найти ошибку