有没有办法避免RTL?

时间:2015-02-23 13:46:47

标签: delphi

我一直在玩Delphi中不允许使用RTL的东西。这是一种dll。

在拆分PE(可移植可执行文件)文件格式之后,我意识到所有PE文件都有"入口点" 。这是Windows在加载模块(exe或dll)后调用的第一件事。

驻留在此入口点的函数的名称及其隐含参数取决于它的PE类型:

  • 动态链接库(* .dll): DllMain

    function DllMain(hinstDLL: HINST; fdwReason: DWORD; 
          lpvReserved: Pointer): BOOL; stdcall;
    
  • 可执行文件(* .exe): WinMain

    function WinMain(hInstance: HINST; hPrevInstance: HINST; 
          lpCmdLine: LPSTR; nCmdShow: Integer): Integer; stdcall;
    
  • 设备驱动程序(* .sys): DriverEntry

    function DriverEntry(DriverObject: PDriverObject; 
          RegistryPath: PUnicodeString): NTSTATUS; stdcall;
    

在Delphi中,此入口点是位于主项目文件中的代码。

对于DLL,可以读取传递给我们的" DllMain"按LoadLibrary。如果在 EntryPointAddress

中放置断点

enter image description here

你可以在堆栈上看到三个参数:

enter image description here

你所要做的就是抓住它们:

library Project1;

function DllMain(hinstDLL: HINST; fdwReason: Cardinal; lpvReserved: Pointer): Integer; stdcall;
begin
    Result := 1; //Windows uses FALSE=0, TRUE=1. Delphi uses False=0, True=-1
end;

begin
    //Code in here is run during DllMain.
    //i.e. DllMain does not return until this code completes.
    asm
        { Get the parameters to DllMain that Windows passed to us:
                [ESP+4] hinstDLL
                [ESP+8] fdwReason
                [ESP+12] lpvReserved
        }
        MOV eax, [ESP+12];  //push lpvReserved onto the stack
        PUSH eax;
        MOV eax, [ESP+8];  //push fdwReason onto the stack
        PUSH eax
        MOV eax, [ESP+4];  //push hinstDLL onto the stack
        PUSH eax;

        CALL DllMain;       //Call our DllMain function
                            //DllMain leaves BOOL result in EAX
   end;
end.

但是有一个RTL

问题是,那里不仅有我的代码。编译器只在之前插入隐藏的代码,而在项目文件中的代码块之后只需

enter image description here

基本上,真正的 DllMain代码包含:

function DllMain(hinstDLL: HINST; fdwReason: Cardinal; lpvReserved: Pointer): LongBool; stdcall;
begin
    SysInit._InitLib(InitTable, hinstDLL, fdwReason, lpvReserved);

    asm
        MOV eax, [ESP+12];  //push lpvReserved onto the stack
        PUSH eax;
        MOV eax, [ESP+8];  //push fdwReason onto the stack
        PUSH eax
        MOV eax, [ESP+4];  //push hinstDLL onto the stack
        PUSH eax;

        CALL DllMain;       //Call our DllMain function
                            //DllMain leaves BOOL result in EAX
   end;

   System._Halt0;
end;

现在,对_InitLib的这个序言调用确实会对试图提取hinstDLLfdwReason的值造成严重破坏;但这不是一个难以克服的问题(例如,您仍然可以在EBP+8+12+16找到它们。

但我的问题是RTL链接到始终不可用的代码。查看导入目录表,您可以看到它需要:

  • user32.dll(例如MessageBoxA
  • kernel32.dll(例如VirtualAllocVirtualFreeCloseHandle

我可以避开RTL吗?

是否有编译器选项或定义可以消除System._InitLibSystem._Halt0的所有内容?或者只是让编译器不要将它们放入:

function DllMain(hinstDLL: HINST; fdwReason: Cardinal; lpvReserved: Pointer): LongBool; stdcall;
begin
   SysInit._InitLib(InitTable, hinstDLL, fdwReason, lpvReserved);

   //...
   System._Halt0;
end;

显然,编译器知道创建它们的是一些特殊的智能。隐含在 EntryPointProcedure 中的内容会发生变化,具体取决于它是还是应用程序二进制文件。

Bonus Chatter:缺少WinMain参数

的情况

记录WinMain入口点以传递四个参数:

function WinMain(hInstance: HINST; hPrevInstance: HINST; 
          lpCmdLine: LPSTR; nCmdShow: Integer): Integer; stdcall;
  • hInstance: HINSTANCE
  • hPrevInstance: HINSTANCE
  • lpCmdLine: LPSTR
  • nCmdShow: Integer

这就是为什么我无法弄清楚为什么这些论点没有传递给EntryPointFunction的原因:

enter image description here

我在堆栈中进一步调试。我试过其他调试器。 Windows只是没有将适当的参数传递给入口点功能。然后我找到the answer

  

操作系统调用没有参数的函数

真正的 Windows .exe入口点功能是:

DWORD CALLBACK RawEntryPoint(void);

又名:

function RawEntryPoint(): DWORD; stdcall;
  

WinMain的参数来自哪里,如果它们没有传递到原始入口点?

     

语言启动代码通过询问操作系统来获取它们:

     
      
  • 可执行文件的实例句柄来自GetModuleHandle(NULL)
  •   
  • 命令行来自GetCommandLine
  •   
  • nCmdShow来自GetStartupInfo
  •   
  • hPrevInstance始终为NULL
  •   

所以这解释了。

3 个答案:

答案 0 :(得分:8)

KOL仍然活着,并且官方网站http://kolmck.net(由我维护)包含有关如何重写系统单元的示例。

答案 1 :(得分:4)

使用stock编译器/ RTL无法实现您的目标。编译器期望存在SystemSysInit单元,并且确实使用这些单元为许多语言功能提供运行时支持。例如,字符串是一种语言功能,它是通过System单元中实现的支持功能实现的。

如果您提供编译器将接受的替换SystemSysInit单元,则可以删除用户模式模块上的依赖项。然而,这不是为了佯装。

由于您似乎希望编写内核模式驱动程序代码,我建议使用FPC。

答案 2 :(得分:1)

当加载DLL并且其主DPR代码开始运行时,您可以从RTL的全局hinstDLL变量中获取SysInit.HInstance值,并且您隐含地知道{{1值必须为fdwReason。在DLL_PROCESS_ATTACH期间无法检索的唯一值是DLL_PROCESS_ATTACH值,因为RTL忽略它而不是将其保存在某处。但是,如果您希望代码对其他lpvReserved值做出反应,并在其他原因状态中接收fdwReason值,那么您所要做的就是分配RTL调用的lpvReserved回调从其内部DllProcEx入口点开始,例如:

DllMain