我能否在Delphi中即时编译代码并执行?

5

能否生成一个字节数组,然后让Windows像正常代码一样执行它?假设我们有一些汇编代码:

inc  ecx

这是程序的一部分。在使用Nasm编译后,我们得到一个EXE文件,其中上述行被转换为类似于以下内容:

00000035 41 

是否有可能创建一个字节数组,填充上述字节并执行-这样增量实际上就会发生?

我已经建立了我的超级简单的解释性语言,但是由于它是解释型的,因此速度相当慢。我不想为此编写真正的编译器,但我想让它更快-实时编译和运行。

3个回答

14
当然。支持数据执行预防的处理器和操作系统可能会遇到问题,但这很容易规避。只需调用 VirtualProtect 将内存块标记为可执行即可。最好使用VirtualAlloc来分配要执行的内存。这样,您就有了一个完整的内存页面专门用于可执行代码。如果您调用VirtualProtect使一些随意分配的内存可执行,则实际上会将整个页面标记为这种方式,因此可能会意外地将某些数据标记为可执行内容。如果该数据受到损害,可能会被执行。这正是DEP旨在保护的内容,因此最好将数据和可执行代码保留在单独的受保护区域中。
请记住,将文本代码转换为机器代码的任务就是编译,因此,如果您不想编写真正的编译器,您可能不想生成机器代码。

谢谢。这是“编译”,但没有所有的EXE头文件、外部库的链接等,所以应该更容易些。 - Tom
1
你仍然需要重新定位(fixup)它。除非你不使用任何数据(或通过在启动时传递到寄存器中的指针间接地寻址),并且只进行近跳转或PIC。 - Marco van de Voort

7
const
   size = 32768;
type
   TFuncInt = function(param: Integer): Integer; // EAX -> EAX
   TByteArray = array[0..size-1] of Byte;
   PByteArray = ^TByteArray;
var
   arr: PByteArray;
   func_param: Integer;
   func_result: Integer;
begin
   arr := VirtualAlloc(nil, size, $3000, $40);
   if arr <> nil then begin
     arr[0] := $40;  // inc EAX
     arr[1] := $C3;  // ret
     func_param := 77;
     func_result := TFuncInt(arr)(func_param);  // 78
     VirtualFree(arr, 0, $8000);
  end;
end;

1
@RobKennedy - 你的代码版本无法编译。在Delphi7中未定义Page_Execute_ReadWrite。你应该手动定义它,或者使用数字值代替。 - Egor Skriptunoff
那就定义它。我不在乎你的开发环境有多过时,不要使用裸数字。对于阅读代码的任何人来说,它们毫无意义。 - Rob Kennedy

1
我认为带有数据执行预防(DEP)功能的现代处理器将禁止此操作。 请注意,有多个用于此类目的的Pascal脚本库可供使用。

我知道类似RemObjects Pascal Script这样的东西。但我不想用Pascal语言编写,而且我需要的是非常简单的东西。 - Tom
3
DEP不能阻止通过VirtualAlloc()或VirtualProtect()明确标记为可执行的内存中的指令执行。像VCL和ATL这样的框架动态分配可执行内存块用作回调函数,DEP对此可以正常工作。 - Remy Lebeau
1
还有一些表达式求值器可以实现这个功能。但它们通常模仿堆栈机器,因此只需要一个带有堆栈指针的寄存器,如果代码是PIC的话。 - Marco van de Voort

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