我一直在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 处放置断点:
可以在堆栈上看到三个参数:
您所要做的就是捕获它们:
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
问题是那里不仅仅是我的代码。编译器在项目文件中的代码块之前和之后插入隐藏代码:
本质上,真实 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
的序言调用确实对尝试提取 hinstDLL
和 fdwReason
的值造成了一些破坏;但这并不是一个无法克服的问题(例如,您仍然可以在 EBP+8
、+12
和 +16
处找到它们)。
但我的问题是 RTL 链接到的代码并不总是可用。查看导入目录表,您可以看到它需要:
user32.dll
(例如MessageBoxA
)kernel32.dll
(例如VirtualAlloc
、VirtualFree
、CloseHandle
)
我可以避免 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:
我在堆栈中越来越深入地进行调试。我尝试了其他调试器。 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 fromGetStartupInfo
hPrevInstance
is alwaysNULL
这就解释了这一点。
最佳答案
KOL依然生机勃勃,官网http://kolmck.net (由我维护)包含如何重写系统单元的示例。
关于delphi - 有没有办法避免RTL?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28675215/