Delphi xe2使用x64编译器编译ASM代码时出错。不支持的语言功能:'ASM'

时间:2015-06-08 18:38:34

标签: delphi assembly 64-bit

正如标题所说,我正面临着使用包含ASM代码的x64编译器编译Delphi XE2项目的问题。当我尝试编译它时,我收到错误“不支持的语言功能:'ASM'”。

我试图从包含ASM代码的过程中删除delphi代码然后我得到“操作码和操作数的无效组合”。

以下是代码的一部分..

type
  TDllLoadInfo = record
    Module: pointer;
    EntryPoint: pointer;
  end;

  TGetProcAddrExInfo = record
    pExitThread: pointer;
    pGetProcAddress: pointer;
    pGetModuleHandle: pointer;
    lpModuleName: pointer;
    lpProcName: pointer;
  end;

  TInjectLibraryInfo = record
    pLoadLibrary: pointer;
    lpModuleName: pointer;
    pSleep: pointer;
  end;



procedure DllEntryPoint(lpParameter: pointer); stdcall;
var
  LoadInfo: TDllLoadInfo;
begin
  LoadInfo := TDllLoadInfo(lpParameter^);
  asm
    xor eax, eax
    push eax
    push DLL_PROCESS_ATTACH
    push LoadInfo.Module
    call LoadInfo.EntryPoint
  end;
end;

procedure GetProcAddrExThread(lpParameter: pointer); stdcall;
var
  GetProcAddrExInfo: TGetProcAddrExInfo;
begin
  GetProcAddrExInfo := TGetProcAddrExInfo(lpParameter^);
  asm
    push GetProcAddrExInfo.lpModuleName
    call GetProcAddrExInfo.pGetModuleHandle
    push GetProcAddrExInfo.lpProcName
    push eax
    call GetProcAddrExInfo.pGetProcAddress
    push eax
    call GetProcAddrExInfo.pExitThread
  end;
end;



procedure InjectLibraryThread(lpParameter: pointer); stdcall;
var
  InjectLibraryInfo: TInjectLibraryInfo;
begin
  InjectLibraryInfo := TInjectLibraryInfo(lpParameter^);
  asm
    push InjectLibraryInfo.lpModuleName
    call InjectLibraryInfo.pLoadLibrary
    @noret:
    mov eax, $FFFFFFFF
    push eax
    call InjectLibraryInfo.pSleep
    jmp @noret
  end;
end;

有没有办法编译这个项目而不会出现任何错误或将asm代码转换为delphi / pascal? 谢谢你的时间。顺便说一句,我不知道ASM。

2 个答案:

答案 0 :(得分:8)

不能在64位的非汇编过程中使用内联汇编,仅在32位中。 Embarcadero的文档中明确说明了这一点:

Converting 32-bit Delphi Applications to 64-bit Windows | Inline Assembly code

  

如果您的应用程序包含内联汇编(ASM)代码,则需要检查ASM代码并进行以下更改:

     
      
  • 64位应用程序不支持将汇编语句与Pascal代码混合。使用Pascal代码或完全用汇编语言编写的函数替换汇编语句   ...
  •   

在您的示例中,没有理由使用内联汇编。您可以(并且应该)使用纯Pascal代码来支持32位和64位:

type
  TDllEntryPointFunc = function(hinstDLL: HINSTANCE; fdwReason: DWORD; lpvReserved: Pointer): BOOL; stdcall;
  TGetModuleHandleFunc = function(const lpModuleName: PChar): HMODULE; stdcall;
  TGetProcAddressFunc = function(hModule: HMODULE; lpProcName: PAnsiChar): Pointer; stdcall;
  TExitThreadFunc = procedure(dwExitCode: DWORD); stdcall;
  TLoadLibraryFunc = function(const lpFileName: PChar): HMODULE; stdcall;
  TSleepFunc = procedure(dwMilliseconds: DWORD); stdcall;

procedure DllEntryPoint(lpParameter: Pointer); stdcall;
var
  LoadInfo: TDllLoadInfo;
  pEntryPoint: TDllEntryPointFunc;
begin
  LoadInfo := TDllLoadInfo(lpParameter^);
  @pEntryPoint := LoadInfo.EntryPoint;
  pEntryPoint(LoadInfo.Module, DLL_PROCESS_ATTACH, nil);
end;

procedure GetProcAddrExThread(lpParameter: Pointer); stdcall;
var
  GetProcAddrExInfo: TGetProcAddrExInfo;
  pGetModuleHandle: TGetModuleHandleFunc;
  pGetProcAddress: TGetProcAddressFunc;
  pExitThread: TExitThreadFunc;
  Module: HMODULE;
  Proc: Pointer;
begin
  GetProcAddrExInfo := TGetProcAddrExInfo(lpParameter^);
  @pGetModuleHandle := GetProcAddrExInfo.pGetModuleHandle;
  @pGetProcAddress := GetProcAddrExInfo.pGetProcAddress;
  @pExitThread := GetProcAddrExInfo.pExitThread;

  Module := pGetModuleHandle(PChar(GetProcAddrExInfo.lpModuleName));
  Proc := pGetProcAddress(Module, PAnsiChar(GetProcAddrExInfo.lpProcName));
  pExitThread(DWORD(Proc));
end;

procedure InjectLibraryThread(lpParameter: Pointer); stdcall;
var
  InjectLibraryInfo: TInjectLibraryInfo;
  pLoadLibrary: TLoadLibraryFunc;
  pSleep: TSleepFunc;
begin
  InjectLibraryInfo := TInjectLibraryInfo(lpParameter^);
  @pLoadLibrary := InjectLibraryInfo.pLoadLibrary;
  @pSleep := InjectLibraryInfo.pSleep;

  pLoadLibrary(PChar(InjectLibraryInfo.lpModuleName));
  repeat
    pSleep(-1);
  until False;
end;

答案 1 :(得分:4)

代码无法直接正确移植到x64,因为它会执行64位指针截断 - 详见下文。

The x64 compiler does not support inline assembly

  

64位应用程序不支持将汇编语句与Pascal代码混合使用。使用Pascal代码或完全用汇编编写的函数替换汇编语句。

这里使用装配是不必要的。我不确定为什么原作者会选择去解决这个问题。通过转换为Pascal来处理此类移植问题总是最好的,这样编译器就可以完成所有工作。

您可以按以下方式编写代码:

type
  TDllLoadInfo = record
    Module: HMODULE;
    EntryPoint: function(hinstDLL: HMODULE; fdwReason: DWORD;
      lpvReserved: Pointer): BOOL; stdcall;
  end;

  TGetProcAddrExInfo = record
    ExitThread: procedure(dwExitCode: DWORD); stdcall;
    GetProcAddress: function(hModule: HMODULE;
      lpProcName: PAnsiChar): Pointer; stdcall;
    GetModuleHandle: function(lpModuleName: PWideChar): HMODULE; stdcall;
    lpModuleName: PWideChar;
    lpProcName: PAnsiChar;
  end;

  TInjectLibraryInfo = record
    LoadLibrary: function(lpFileName: PWideChar): HMODULE; stdcall;
    lpModuleName: PWideChar;
    Sleep: procedure(dwMilliseconds: DWORD); stdcall;
  end;

procedure DllEntryPoint(lpParameter: pointer); stdcall;
var
  LoadInfo: ^TDllLoadInfo absolute lpParameter;
begin
  LoadInfo.EntryPoint(LoadInfo.Module, DLL_PROCESS_ATTACH, nil);
end;

procedure GetProcAddrExThread(lpParameter: pointer); stdcall;
var
  GetProcAddrExInfo: ^TGetProcAddrExInfo absolute lpParameter;
  ModuleHandle: HMODULE;
  ProcAddress: Pointer;
begin
  ModuleHandle := GetProcAddrExInfo.GetModuleHandle(GetProcAddrExInfo.lpModuleName);
  ProcAddress := GetProcAddrExInfo.GetProcAddress(ModuleHandle,
    GetProcAddrExInfo.lpProcName);
  GetProcAddrExInfo.ExitThread(DWORD(ProcAddress)); // !!!! x64 pointer truncation !!!!
end;

procedure InjectLibraryThread(lpParameter: Pointer); stdcall;
var
  InjectLibraryInfo: ^TInjectLibraryInfo absolute lpParameter;
begin
  InjectLibraryInfo.LoadLibrary(InjectLibraryInfo.lpModuleName);
  while True do // rather pointless to loop ....
    InjectLibraryInfo.Sleep(INFINITE);
end;

代码假定您已获得W后缀Unicode API函数的函数指针。如果没有,请使用PAnsiChar代替PWideChar

然而,在这一点上,我们需要评估并考虑我们刚刚做了什么。我们试图将64位指针推入32位DWORD线程退出代码。这不一定适合。如果将库加载到4GB以上的地址,那么您将遭受指针截断。

因此,这里的底线是您无法正确将此代码移植到x64。您需要找到此代码的变体,该变体能够从线程返回64位值。但它必须以不同的方式这样做,因为你不能将64位值放入线程返回值。