有没有办法避免RTL?

Ian*_*oyd 4 delphi

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

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

名称的功能,位于此入口点,并且它的隐含参数,取决于PE的一种,它是:

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

在DLL的情况下,可以读取传递给我们的"DllMain"的参数LoadLibrary.如果在EntryPointAddress上放置一个断点:

在此输入图像描述

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

在此输入图像描述

您所要做的就是捕获它们:

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.
Run Code Online (Sandbox Code Playgroud)

但是有一个RTL

问题是那里不仅仅是我的代码.编译器在项目文件中的代码块之前之后插入隐藏代码:

在此输入图像描述

基本上,真正的 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;
Run Code Online (Sandbox Code Playgroud)

现在,这个序言调用_InitLib做一些发泄与破坏试图拉的价值观hinstDLLfdwReason; 但是这不是一个不可克服的问题(例如,你仍然可以在找到他们EBP+8,+12+16).

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

  • user32.dll(例如MessageBoxA)
  • kernel32.dll(例如VirtualAlloc,VirtualFree,CloseHandle)

我能避开RTL吗?

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

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

   //...
   System._Halt0;
end;
Run Code Online (Sandbox Code Playgroud)

对于知道创建它们的编译器来说,这显然是一个特殊的智能.而且,如果它是一个应用程序二进制文件,那么隐含在EntryPointProcedure中的内容会发生变化.

Bonus Chatter:缺少WinMain参数的情况

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

function WinMain(hInstance: HINST; hPrevInstance: HINST; 
          lpCmdLine: LPSTR; nCmdShow: Integer): Integer; stdcall;
Run Code Online (Sandbox Code Playgroud)
  • hInstance: HINSTANCE
  • hPrevInstance: HINSTANCE
  • lpCmdLine: LPSTR
  • nCmdShow: Integer

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

在此输入图像描述

我正在进一步调试并进一步回到堆栈中.我试过其他调试器.Windows只是没有将适当的参数传递给入口点功能.然后我找到了答案:

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

真正的Windows .exe入口点函数为:

DWORD CALLBACK RawEntryPoint(void);
Run Code Online (Sandbox Code Playgroud)

又名:

function RawEntryPoint(): DWORD; stdcall;
Run Code Online (Sandbox Code Playgroud)

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

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

  • 可执行文件的实例句柄来自 GetModuleHandle(NULL)
  • 命令行来自 GetCommandLine
  • 而且nCmdShow来自GetStartupInfo
  • hPrevInstance 总是 NULL

这就解释了这一点.

小智 8

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


Dav*_*nan 3

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

如果您提供编译器可接受的替换SystemSysInit单元,则可以删除对用户模式模块的依赖关系。然而,这并不是为了假心。

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