在Delphi中,我该如何直接执行内存中的代码?

3

有没有可能模仿 loadlibrary 函数呢?我想从BLOB字段中加载一个库,而不需要先将其写入临时文件,并且需要一种不依赖于特定版本的Delphi编译器或Windows系统的解决方案,也不会触发反病毒软件。


2
“并且不会触发防病毒软件” -- 不幸的是,一些防病毒软件实际上对使用Delphi编写的程序产生警告,仅因为“使用Delphi编写”就成了可疑的部分。 - user743382
这是官方不支持的。虽然有一些黑客技巧可用,但它们真的只是黑客技巧。 - David Heffernan
加密的正确方式是将算法代码放入应用程序中,但保护密钥。看起来你试图保护算法本身。我不认为这比仅保护密钥更强大。无论如何,一旦加载到内存中,您就没有保护措施了。黑客可以读取您的内存。您需要让密钥保持在安全设备上。 - David Heffernan
3个回答

7
可以的,而且你无需使用loadlibrary函数来执行内存中的代码 - 你需要使用VirtualAlloc函数分配一个内存,并将PAGE_EXECUTE标志设置。


更新:这里是32位Delphi中内存中执行代码的快速笨办法演示 - 我只测试了它是否工作:

type
  TIncMe = procedure(var I: Integer);

var
  IncMeProc: TIncMe;

procedure IncMe(var I: Integer);
begin
  Inc(I);
end;

procedure CopyIncMe;
var
  Size: LongWord;
  Tmp: Pointer;

begin
  Size:= LongWord(@CopyIncMe) - LongWord(@IncMe);
  Tmp:= VirtualAlloc(nil, Size, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  Move(Pointer(@IncMe)^, Tmp^, Size);
  IncMeProc:= Tmp;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  J: Integer;

begin
  J:= 0;
  CopyIncMe;
  while J < 10 do begin
    IncMeProc(J);
    ShowMessage(IntToStr(J));
  end;
  VirtualFree(@IncMeProc, 0, MEM_RELEASE);
end;

1
如果这是一个dll文件,你需要做更多的工作。 - David Heffernan
我想我可以处理这个。我有一个解密函数,它从数据库中读取BLOB字段,将其写入临时文件,然后使用loadlibrary加载它。通过上述代码,我可以将我的加密函数存储在智能卡中,并直接将其加载到内存中。 - iMan Biglari
@iManBiglari - 是的,类似的代码被用于软件保护 - 一个可执行文件包含被加密的代码,该代码通过用户提供的密钥解密并在内存中执行。 - kludg
5
如果储存的代码需要调用任何外部函数,那么就会出现问题,因为代码不知道这些函数地址的相对偏移量。修复地址是LoadLibrary执行的任务之一。 - Rob Kennedy
2
+1 是的,你应该小心不要处理外部代码;只有局部变量的函数是最适合移动到内存中的候选对象。 - kludg
显示剩余2条评论

5

我用它写了一个快速的模拟应用程序,它运行良好... 这个周末我打算尝试加载一些带有初始化/终止代码的复杂DLL,并在我知道我的客户正在使用的几个Windows版本上进行检查。但似乎这个库很管用。非常感谢 :-) - iMan Biglari
3
请注意,这在微软官方不受支持。这是一种技巧性的方法,请预料将来可能会出现问题。 - David Heffernan

4

delphi.about.com上有一篇文章,展示了如何从资源中加载dll。

首先将资源加载到内存中,然后使用Memory Module从资源中加载dll。

您可以使用数据库或其他任何来源来加载dll,只需将其保存在内存流中,然后使用以下代码加载和执行dll函数,这看起来非常像调用dll的“正常”代码:

var
  btMM: PBTMemoryModule;
begin
  btMM := BTMemoryLoadLibary(mp_DllData, m_DllDataSize);
  try
    if btMM = nil then Abort;
    @m_TestCallstd := BTMemoryGetProcAddress(btMM, 'TestCallstd');
    if @m_TestCallstd = nil then Abort;
    m_TestCallstd('This is a Dll Memory call!');
  except
    Showmessage('An error occoured while loading the dll: ' + BTMemoryGetLastError);
  end;
  if Assigned(btMM) then BTMemoryFreeLibrary(btMM);
end;

我在[link]https://nodeload.github.com/jasonpenny/democode/zipball/master[/link]下载了演示版,但在BTMemoryModule.pas的第461行失败:`l_successfull := l_DllEntry(Cardinal(l_code), DLL_PROCESS_ATTACH, nil);`,出现访问冲突。我认为这段代码是特定于编译器版本和/或Windows版本的。 - iMan Biglari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接