Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2008.04.20;
Скачать: [xml.tar.bz2];

Вниз

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

 
Пробегал...   (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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.55 MB
Время: 0.041 c
2-1206307378
batya-x
2008-03-24 00:22
2008.04.20
TrackBar и OnClick


2-1206722314
Che
2008-03-28 19:38
2008.04.20
Заблокировка клавиатуры и рестарт компьютера.


15-1204757782
Германн
2008-03-06 01:56
2008.04.20
Currency and date/time formatting variables


2-1206074528
atomAltera
2008-03-21 07:42
2008.04.20
Как программно вывести контекстное меню какого нибудь файла?


2-1206527869
Gavrila
2008-03-26 13:37
2008.04.20
PopupMenu - определить какой Item вызвал событие OnClik





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский