我正在尝试从我的程序动态加载exe文件,并从动态加载的exe运行SomeProcedure。这是我在加载的exe中执行的操作 - library.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。我做错了什么?
答案 0 :(得分:6)
据我所知,你所尝试的是不可能的。您不能使用LoadLibrary加载.exe文件,然后调用其导出的函数。您只能将一个.exe文件加载到进程中。您需要将功能移动到库,COM服务器或其他解决方案中。
正如Sertac指出的那样,documentation确实涵盖了这一点:
LoadLibrary也可用于加载其他可执行模块。例如,该函数可以指定.exe文件 获取可在FindResource或LoadResource中使用的句柄。但是,不要使用LoadLibrary来运行.exe文件。而是使用CreateProcess函数。
您可以将GetProcAddress与可执行文件的模块句柄一起使用。但是,您必须通过调用GetModuleHandle(0)来获取模块句柄。
答案 1 :(得分:1)
大卫已经指出这几乎是不可能的。我不会说不是不可能。只是为了理解你可以从理论上调用CreateProcess,然后挂钩它的调用和后面的调用。其中一个电话也是ZwCreateSection。很久以前我玩过这样的东西,理论上可以做到这一点。 CreateProcess创建一个空的进程上下文,然后用其他Zw / Nt kernell调用填充。知道你可以提供内容的电话。
但那是理论,甚至是黑客入侵操作系统。正如大卫所指出的那样,不可能以理智和记录的方式做到这一点。您可以做什么hovewer将exe作为资源提取到临时文件并执行它。然后,您可以等待它结束,然后删除该文件。这是唯一的方法。要在RAM中完成所有操作是不行的。
编辑:
以下是可以使用technique的文章。但它是一个丑陋的黑客:)
答案 2 :(得分:-1)
“现在我有一个AV来调用Proc” - 你忘了在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相同的方式调用它。