Undocumented LdrXXX 例程
 

作者:陆麟
转载请征得作者同意.
2001.8.6



当我们写程序的时候, 通常会写有一些纯算法类的库. 我通常把这些库编译为一个DLL. 当写作NT NATIVE APPLICATION时候, 如果需要动态连接这个DLL怎么办? 为了达到目的, 我花了1天时间研究了一下运用NTDLL的LdrXXX例程, NTDLL.DLL的LdrXXX乃是进程线程初始化时, NT操作系统用于为应用程序准备WIN32环境所使用的例程. 这里区分一下到底什么是核心.

NTOSKRNL.EXE是什么?
KERNEL32.DLL是什么?
NTDLL.DLL又是什么?

通常情况下, 大多数人所说的核心是WIN32核心. 也就是由KERNEL32.DLL/USER32.DLL/GDI32.DLL所构成的核心. 这的确是核心. 我们平时编写成员所用到的平台---WIN32---的核心就是他们. 在任何情况下, 都会有另外一个核心:操作系统的核心. WIN9X操作系统的核心被BUILD在VMM32.VXD中, NT操作系统的核心被BUILD在NTOSKRNL.EXE中. 操作系统核心的基本特征就是对CPU的调度. 只有它才能决定CPU给谁. 其他的特征在各种操作系统中会有不同的体现. 有些操作系统核心愿意管理一些基本设备, 而有些操作系统的核心则不负责任何设备的管理. 有些OS把对内存管理纳入了核心, 而有些OS则不.

在NT下, NTOSKRNL.EXE由OS核心和对象管理器,进程管理器,IO管理器等组件构成. 是核心和一堆独立于平台的算法的组合. 这些算法包括对设备管理等, 无论什么设备, 调度者在NTOSKRNL.EXE里, 设备要运行, 涉及的平台相关的例程有硬件抽象层HAL.DLL提供. 核心和OS管理的辅助代码(executive层)位于CPU的RING0.
NTDLL.DLL是各种环境子系统和RING0部分代码(包括OS核心和excutive,设备运行控制代码)进行沟通的桥梁. 所有的环境子系统都通过NTDLL.DLL进行和RING0代码交互.
KERNEL32.DLL,GDI32.DLL,USER32.DLL在NT下仅是WIN32环境必备的库而已, 他们提供WIN32API和NTDLL.DLL的转换层. 并且负责WIN32平台特定属性的实现.

下面回到正题. 经跟踪获得的DLL类NATIVE API原形定义如下:

//load dll
//Flag seems always be 0
typedef NTSTATUS (NTAPI *TLdrLoadDll)(WCHAR *PathEnv,DWORD *Flag,LSA_UNICODE_STRING *DllName,HINSTANCE *hInstance);
PathEnv在DLL没有指定路径时用到. 如果DllName已经指定了绝对路径. PathEnv就没有用了.
LSA_UNICODE_STRING实际就是UNICODE_STRING,我定义为LSA_UNICODE_STRING不过想用起来方便点. 因为LSA_UNICODE_STRING在Ntsecapi.h中已经被定义了.

//get dll handle
//dw1 seems always be 1, dw2 seems always be 0,
typedef NTSTATUS (NTAPI *TLdrGetDllHandle)(DWORD dw1,DWORD dw2,LSA_UNICODE_STRING *DllName,HINSTANCE *hInstance);
注意,此处DLLNAME一定是不包含路径的DLL名. 否则永远不能得到正确的结果.

//Unload a dll
typedef NTSTATUS (NTAPI *TLdrUnLoadDll)(HINSTANCE hInstance);
这个函数没有什么号解释的了.