如何在内存中获取方法的整个代码,以便在运行时计算其哈希值?
我需要制作一个这样的函数:
type
TProcedureOfObject = procedure of object;
function TForm1.CalculateHashValue (AMethod: TProcedureOfObject): string;
var
MemStream: TMemoryStream;
begin
result:='';
MemStream:=TMemoryStream.Create;
try
//how to get the code of AMethod into TMemoryStream?
result:=MD5(MemStream); //I already have the MD5 function
finally
MemStream.Free;
end;
end;
我使用Delphi 7。
编辑: 感谢Marcelo Cantos& gabr指出由于编译器优化没有找到过程大小的一致方法。感谢Ken Bourassa提醒我风险。目标程序(我想计算哈希的程序)是我自己的,我不会从那里调用另一个例程,所以我可以保证它不会改变。
在阅读了关于$ O指令的答案和Delphi 7帮助文件后,我有了一个想法。
我会像这样制作目标程序:
procedure TForm1.TargetProcedure(Sender: TObject);
begin
{$O-}
//do things here
asm
nop;
nop;
nop;
nop;
nop;
end;
{$O+}
end;
程序结束时的5个成功nop
将像书签一样。人们可以用gabr的技巧预测程序的结束,然后扫描附近的5个nops,找出希望正确的大小。
现在,虽然这个想法听起来值得一试,但是......呃......不知道如何将它用于Delphi代码。我没有低级编程方面的经验,比如如何获取入口点,并在扫描5 TMemoryStream
时将目标过程的整个代码放入nop
。
如果有人能给我一些实际的例子,我将非常感激。
答案 0 :(得分:2)
答案 1 :(得分:2)
马塞洛已经正确地指出这一般是不可能的。
通常的解决方法是使用要计算哈希值的方法的地址以及下一个方法的地址。目前,编译器以与源代码中定义的顺序相同的顺序布局方法,这种技巧有效。
请注意,减去两个方法地址可能会给你一个稍大的结果 - 第一个方法实际上可能会在下一个方法开始之前结束几个字节。
答案 2 :(得分:2)
我能想到的唯一方法是打开TD32 debuginfo,然后尝试使用JCLDebug查看是否可以在debuginfo中找到它的长度。重定位不应影响长度,因此二进制文件中的长度应与mem中的长度相同。
另一种方法是扫描代码以获取ret或ret操作码。这不太安全,但可能至少可以保护部分功能,而不必乱用debuginfo。
潜在的交易破坏者虽然是尾部调用优化的短程序(i他们跳而不是退货)。但我不知道Delphi是否这样做。
答案 3 :(得分:2)
我通过让Delphi生成MAP文件并根据起始地址按升序排序符号来实现这一目的。然后,每个过程或方法的长度是下一个符号起始地址减去该符号起始地址。这很可能像这里提出的其他解决方案一样脆弱,但是我现在正在生产这个代码,到目前为止它对我来说还算合适。
我可以在第3615行找到here读取地图文件并计算大小的实现(TEditorForm.RemoveUnusedCode)。
答案 4 :(得分:-1)
即使你能实现它,也需要注意一些事情......
即使函数本身没有改变,哈希也会多次改变。
例如,如果您的函数调用自上次构建以来更改地址的另一个函数,则哈希将更改。我认为如果你的函数以递归方式调用自身并且自上次构建以来你的单元(不一定是你的函数)发生了变化,哈希也可能会改变。
至于如何实现,gabr's suggestion似乎是最好的......但它确实很容易突破。