动态加载exe文件

3
我正在尝试从我的程序动态加载一个exe文件,并从该动态加载的exe中运行SomeProcedure。这是我在加载的exe库.exe中所做的操作。
interface    

procedure SomeProcedure; stdcall;

implementation    

procedure SomeProcedure;
begin
  ShowMessage('Ala has a cat');
end;

这里是我的exe文件,它加载了library.exe并尝试运行其中的SomeProcedure函数。

type
  THandle = Integer;
  TProc = procedure();

var
  AHandle: THandle;
  Proc: TProc;

procedure TForm1.Button1Click(Sender: TObject);
begin
  AHandle := LoadLibrary('library.exe');
  if AHandle <> 0 then begin
    @Proc := GetProcAddress(AHandle, 'SomeProcedure');
    if @Proc <> nil then 
      try    
        Proc;
      finally
        FreeLibrary(AHandle);
      end;
    end;
  end;
end;

很遗憾它不起作用了 - AHandle有一个地址,但是GetProcAddress总是返回nil。我做错了什么?


5
你的项目文件里有一行 exports SomeProcedure; 吗? - Sertac Akyuz
说实话,我不知道 export 指令是干嘛用的... - Sertac Akyuz
3
@Sertac: 如果我没记错的话,它被忽略了。很久以前它曾经有过一个含义。 - Andreas Rejbrand
@SertacAkyuz 这并没有产生任何区别 :( - JustMe
1
@Gerry 在可执行文件中,exports指令与动态链接库中的作用相同。 - David Heffernan
显示剩余5条评论
3个回答

6
据我所知,您正在尝试的是不可能的。您不能使用LoadLibrary加载一个.exe文件,然后调用它的导出函数。您只能将一个.exe文件加载到进程中。您需要将功能移动到库、COM服务器或其他解决方案中。
正如Sertac所指出的那样,文档也涵盖了这一点:

LoadLibrary也可以用于加载其他可执行模块。例如,该函数可以指定一个.exe文件以获取一个句柄,该句柄可以在FindResource或LoadResource中使用。但是,不要使用LoadLibrary运行一个.exe文件。相反,请使用CreateProcess函数。

您可以使用具有可执行文件模块句柄的GetProcAddress。但是,您必须通过调用GetModuleHandle(0)来获取模块句柄,例如。

2
这个回答当然是正确的,它在 LoadLibrary 的文档中甚至有所暗示: “..不要使用 LoadLibrary 运行 .exe 文件..”。我放弃了尝试理解它失败的机制;IAT被搞砸了或者甚至在模块边界传递的指针指向了无处。简单的事实是加载器无法帮助这种情况。 - Sertac Akyuz

1
正如David所指出的,这几乎是不可能的。我不会说完全不可能,仅为了理解,你可以在理论上调用CreateProcess并钩取其调用及其后续调用之一ZwCreateSection。我很久以前就研究过这类东西,理论上可以做到。CreateProcess创建一个空的进程上下文,然后填充其他Zw / Nt kernell调用。如果知道调用方式,就可以提供内容。

但那是理论,并且即使如此也是侵入操作系统的黑客行为。如David所指出的,在合理和记录的方法中是不可能做到的。然而,您可以将exe提取为临时文件资源,然后执行它。然后等待它结束,之后再删除文件。这是唯一的方法。要全部在RAM中完成是不可行的。

编辑:

这里有一篇关于可以使用的技术文章。 但这是一个丑陋的黑客行为 :)


-1

“现在我在调用Proc时出现了AV” - 你在TProc的定义中忘记了定义“stdcall”。

刚刚创建了一个小示例,演示如何从另一个EXE文件中加载/执行导出函数。

应用程序1:

program Exe4Export;
uses
  Vcl.Forms,
  Unit3 in 'Unit3.pas' {Form3},
  ExportTypes in 'ExportTypes.pas';

{$R *.res}

function Test:integer; stdcall; export;
begin
  result := 7;
end;

exports
  Test;

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm3, Form3);
  Application.Run;
end.

应用程序2:

type
  TExtProc = function: integer; stdcall;

procedure TForm3.FormCreate(Sender: TObject);
var
  h: HMODULE;
  p: TExtProc;
begin
  h := LoadLibrary('Exe4Export.exe');
  p := GetProcAddress(h, 'Test');
  if assigned(p) then
    caption := IntToStr(p);
  FreeLibrary(h);
end;

所以app2将app1加载到自己的地址空间中(是的,对于DLL来说是可能的),然后获取函数的地址,并以与DLL相同的方式调用它。


1
你的示例之所以能够运行,是因为exe文件什么也没做。一旦你尝试做任何事情,它就会崩溃。 - David Heffernan
2Sertac Akyuz:使用“string”不是一个好主意,因为它是托管对象,而且很棘手(对于DLL也是如此)。PChar只是指针,我们在调用进程的相同地址空间中加载EXE,所以使用PChar将是可以的(对于DLL也是如此)。 - Andrei Galatyn
David Heffernan:示例只是一个示例。它应该表明可以以与DLL相同的方式加载EXE并从该EXE调用过程。而示例确实做到了这一点。这就是所问的问题,不是吗?您可以在以这种方式加载的EXE中做什么-这取决于(对于DLL也是如此)。也许您可以提供一些示例来演示EXE的缺点?我猜我们使用Win API的许多系统函数实际上是在系统EXE中实现的。 - Andrei Galatyn
@Andrei - 嗯,这就是为什么我提供了pchar替代方案。你试过吗?如果您没有对加载的可执行文件的图像基础有一定的了解,那么您将收到垃圾数据。只需尝试一下。 - Sertac Akyuz
@Sertac Akyuz:看来你是对的。没有办法从另一个进程中使用导出函数(至少没有办法在合理的时间内实现并保证结果)。 - Andrei Galatyn
显示剩余3条评论

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