2014-01-30 3 views
1
procedure DoSomething; 
var 
    MyAnonymousProcedure : TProc; 
begin 
    //assign an anonymous procedure to a variable. 
    MyAnonymousProcedure := procedure 
    begin 
    Foo; 
    end; 
    MyAnonymousProcedure(); //Call the newly assigned procedure. 



    // do the same thing again but with a different anonymous method. 
    MyAnonymousProcedure := procedure 
    begin 
    Bar; 
    end; 
    MyAnonymousProcedure(); 
end; 

В приведенном выше коде есть две анонимные процедуры. В свою очередь, они назначаются одной и той же переменной TProc. Код в каждой анонимной процедуре явно отличается. Есть ли способ найти исполняемый код, который ссылается на переменные MyAnonymousProcedure? Думаю, это будет память. Из этого можно затем вычислить хэш из исполняемого кода , найденного в этой ячейке памяти?Можно ли получить хэш реализации анонимной подпрограммы?

+1

Может быть, я жил под скалой для 20 лет, но что значит «рассчитать хэш»? Вы хотите, чтобы вы запускали исходный код для этой процедуры через какой-то алгоритм контрольной суммы? Не могли бы вы объяснить, почему. Я, конечно, открыт для изучения новых вещей, и мне любопытно. – Sam

+1

«Вы хотите, чтобы вы запускали исходный код для этой процедуры через какой-то алгоритм контрольной суммы?» Да, точно. – Shannon

+1

Интересно, но * почему *? (просто любопытно) – Sam

ответ

3

Есть ли способ найти исполняемый код MyAnonymousProcedure ссылки на переменные?

Всегда есть «способ», но в этом случае сложно.

Первый анонимный метод можно рассматривать как ссылку на интерфейс с одним методом Invoke, как объясняется Barry Kelly.

Применяя идею кода мы получаем:

procedure MethRefToProcPtr(const MethRef; var ProcPtr); 
type 
    TVtable = array[0..3] of Pointer; 
    PVtable = ^TVtable; 
    PPVtable = ^PVtable; 
begin 
    // 3 is offset of Invoke, after QI, AddRef, Release 
    TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3]; 
end; 

К сожалению, значение ProcPtr возвращается не то, что вы, вероятно, хотите - это адрес кода заглушки, который фиксирует интерфейсную ссылку (преобразует ссылку на интерфейс к ссылке на объект) и переходит к адресу, который мы ищем. Если проследить код, на который указывает ProcPtr вы найдете что-то вроде этого (Delphi XE, 32-бит):

 add eax,-$10 
    jmp FooBar 

и на FooBar адрес, который вы найдете

 call Foo 

или

 call Bar 

в зависимости от текущего значения вашего анонимного метода.

Я предполагаю, что единственный способ получить адрес FooBar - это проанализировать инструкцию ассемблера jmp.


Вот код, который я использовал для моих экспериментов:

procedure Foo; 
begin 
    Writeln('Foo'); 
end; 

procedure Bar; 
begin 
    Writeln('Bar'); 
end; 

procedure MethRefToProcPtr(const MethRef; var ProcPtr); 
type 
    TVtable = array[0..3] of Pointer; 
    PVtable = ^TVtable; 
    PPVtable = ^PVtable; 
begin 
    // 3 is offset of Invoke, after QI, AddRef, Release 
    TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3]; 
end; 

procedure DoSomething; 
var 
    MyAnonymousProcedure : TProc; 
    MyProc : procedure; 

begin 
    //assign an anonymous procedure to a variable. 
    MyAnonymousProcedure := procedure 
    begin 
    Foo; 
    end; 
// MyAnonymousProcedure(); //Call the newly assigned procedure. 

    MethRefToProcPtr(MyAnonymousProcedure, MyProc); 
    Writeln(Format('%p', [@MyProc])); 
    Writeln(Format('%p', [@Foo])); 
    MyProc; 

    // do the same thing again but with a different anonymous method. 
    MyAnonymousProcedure := procedure 
    begin 
    Bar; 
    end; 
// MyAnonymousProcedure(); 

    MethRefToProcPtr(MyAnonymousProcedure, MyProc); 
    Writeln(Format('%p', [@MyProc])); 
    Writeln(Format('%p', [@Bar])); 
    MyProc; 
end; 
+0

Это работает только в вашем случае. Анонимные методы называются методами, потому что они являются методами. Таким образом, они состоят из кода и указателя данных. Что важно при захвате переменных (поэтому метод stub делает add eax - для изменения ссылки на интерфейс, какие анонимные методы находятся под капотом для компилятора, сгенерированного ссылкой на объекты за кулисами). –

3

В дополнении к другому ответу здесь является процедурой, которая преобразует компилятор метод окурка, который фиксирует EAX к «реальному» метод генерируемого компилятором класса для анонимного метода.

procedure MethodStubToMethod(const Method; var Result); 
var 
    offset: ShortInt; 
begin 
    offset := PByte(TMethod(Method).Code)[2]; 
    TMethod(Result).Code := PByte(TMethod(Method).Code) + 3; 
    TMethod(Result).Data := PByte(TMethod(Method).Data) + offset; 
end; 

Это простая и наивная реализация, которая предполагает, что смещение никогда не будет больше, чем один байт (что только произойдет, если у вас есть сотни различных анонимных методов в той же процедуре (например, у вас есть 2 в исходном источнике в вопросе).

Он предполагает расположение заглушки, как это (что это для анонимных методов AFAIK)

add eax, offset 
jmp address 

Тогда вы можете написать:

procedure MethRefToProcPtr(const MethRef; var ProcPtr); 
type 
    TVtable = array[0..3] of Pointer; 
    PVtable = ^TVtable; 
    PPVtable = ^PVtable; 
begin 
    // 3 is offset of Invoke, after QI, AddRef, Release 
    TMethod(ProcPtr).Code := PPVtable(MethRef)^^[3]; 
    TMethod(ProcPtr).Data := Pointer(MethRef); 
end; 

procedure DoSomething; 
var 
    MyAnonymousProcedure: TProc; 
    Method: procedure of object; 
begin 
    //assign an anonymous procedure to a variable. 
    MyAnonymousProcedure := procedure 
    begin 
    Foo; 
    end; 
    MyAnonymousProcedure(); //Call the newly assigned procedure. 
    MethRefToProcPtr(MyAnonymousProcedure, Method); // 
    Method(); //same as calling the anonymous method 
    MethodStubToMethod(Method, Method) 
    Method(); // now we are calling the method directly on the object  
end; 
Смежные вопросы