delphi - 有没有办法避免RTL?

标签 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,可以读取由 LoadLibrary 传递给“DllMain”的参数。如果您在 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._InitLib 和 System._Halt0 的所有内容?或者只是让编译器不将它们放入:

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

   //...
   System._Halt0;
end;

对于知道创建它们的编译器来说,这显然是一种特殊的智慧。 EntryPointProcedure 中隐式放置的内容会发生变化,具体取决于它是Library 还是Application 二进制文件。

奖励 Chatter:缺少 WinMain 参数的情况

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

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

这就是为什么我无法弄清楚为什么参数没有被传递给 EntryPointFunction:

enter image description here

我在堆栈中越来越深入地进行调试。我尝试了其他调试器。 Windows 只是没有将适当的参数传递给入口点函数。然后我发现the answer :

The operating system calls the function with no parameters

真实 Windows .exe 入口点函数是:

DWORD CALLBACK RawEntryPoint(void);

又名:

function RawEntryPoint(): DWORD; stdcall;

Where do the parameters to WinMain come from, if they aren't passed to the raw entry point?

The language startup code gets them by asking the operating system:

  • the instance handle for the executable comes from GetModuleHandle(NULL)
  • the command line comes from GetCommandLine
  • and the nCmdShow comes from GetStartupInfo
  • hPrevInstance is always NULL

这就解释了这一点。

最佳答案

KOL依然生机勃勃,官网http://kolmck.net (由我维护)包含如何重写系统单元的示例。

关于delphi - 有没有办法避免RTL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28675215/

相关文章:

delphi - 在 Delphi 7 中加载仅 CR 分隔的文件文本时如何解决问题?

delphi - 从 Delphi 的套接字读取换行符

delphi - 无法覆盖 TCollection.Notify : Declaration of Notify differs from previous declaration

delphi - WIA 扫描时跳过设备选择对话框

delphi - 使用方法的嵌套过程作为 winapi 回调是否安全?

delphi - 如何释放 Delphi Prism 中对象的数组列表?

delphi - 不使用节读取值

c++ - 允许图像角点坐标且适用于非网络语言的 map API

delphi - 为什么 I/O 错误无法引发异常?

delphi - 调整表单大小并保持纵横比