NtDll真的导出C运行时function,我可以在我的应用程序中使用这些function吗?

我正在查看Windows 10计算机上的NtDll导出表,发现它导出了标准的C运行时函数,如memcpysprintfstrlen等。

这是否意味着我可以在运行时通过LoadLibraryGetProcAddressdynamic调用它们? 这是保证是每个Windows版本的情况下?

如果是这样,可以完全删除C运行时库(仅使用NtDll的CRT函数),因此使我的程序更小?

完全没有理由调用这些由NtDll导出的未公开的函数。 Windows将所有基本的C运行时功能导出为标准系统库(即coreel32)中的文档包装。 如果你绝对不能链接到C运行库* ,那么你应该调用这些函数。 对于内存,你有基本的HeapAllocHeapFree (或者也许VirtualAllocVirtualFree ), ZeroMemoryFillMemoryMoveMemoryCopyMemory等。对于字符串操作,重要的CRT函数都在那里,以llstrlenlstrcatlstrcpylstrcmp等等。奇怪的是wsprintf (和它的兄弟wvsprintf ),它不但有不同的前缀,而且也不支持浮点值(在这些时代,Windows本身没有浮点代码函数首先被导出并记录下来)。还有其他一些辅助函数,也就是在CRT中复制功能,如IsCharLowerCharLowerCharLowerBuff等。

这是一个旧的知识库文章,它记录了一些C运行时函数的Win32等价物 。 如果重新实现CRT的功能,可能还需要其他相关的Win32函数,但这些是直接的,直接替换的。

其中一些是操作系统的基础设施所绝对需要的,并且将由任何CRT实现在内部调用。 这个类包括HeapAllocHeapFree这些是操作系统的责任。 一个运行时库只包装这些,提供了一个很好的标准C接口和一些其他细节的基本操作系统级的细节之上。 其他的,比如字符串操作函数,只是在CRT的一个内部Windows版本上的导出包装器(除了它是一个真正旧版本的CRT,在历史上的某个时间被修复,除了可能已经修补的主要安全漏洞之外)这些年来)。 还有一些是几乎完全多余的,或者像ZeroMemoryMoveMemory ,但实际上是导出的,所以它们可以在没有C运行库的环境中使用,就像传统的Visual Basic(VB 6)一样。

同样有趣的是,许多“简单的”C运行时库函数都是由Microsoft(和其他供应商)的编译器作为内部函数来实现的 ,并有特殊的处理。 这意味着他们可以高度优化。 基本上,相关的目标代码直接在你的应用程序的二进制文件中直接发送,避免了需要潜在的昂贵的函数调用。 允许编译器生成类似于strlen内联代码,这种代码将始终被调用,几乎无疑会导致更好的性能,而不必为其中一个导出的Windows API支付函数调用的代价。 编译器没有办法“内联” lstrlen ; 它就像任何其他函数一样被调用。 这可以让您回到速度和尺寸之间的经典折衷。 有时一个较小的二进制更快,但有时不是。 不必连接CRT将产生一个较小的二进制,因为它使用函数调用,而不是内联实现,但在一般情况下可能不会产生更快的代码。

*但是,由于各种原因,您确实应该链接到与编译器捆绑在一起的C运行时库,其中最重要的是可以通过更新版本的运行时库将安全更新分发到所有版本的操作系统。 你必须有一个非常好的理由不使用CRT,例如,如果你正在尝试构建世界上最小的可执行文件。 而没有这些功能将只是你的第一个障碍。 CRT为你处理了很多东西,你通常不需要考虑,比如启动和运行这个进程,建立一个标准的C或C ++环境,解析命令行参数,运行静态初始化器,实现构造器和析构函数(如果您正在编写C ++),支持结构化异常处理(SEH,也用于C ++异常)等等。 我已经得到了一个简单的C应用程序来编译,而不依赖于CRT,但它花了不少的手段,我当然不会推荐它远程认真的。 Matthew Wilson 很久以前写了一篇关于避免Visual C ++运行库的文章 。 它基本上是过时的,因为它着重于Visual C ++ 6开发环境,但是很多大的图片仍然是相关的。 我清楚地记得Matt Pietrek也在很久以前的微软杂志上写了一篇关于这方面的文章,但是我在网上任何地方都找不到它的副本(所有链接似乎已经死了)。

如果您只是需要将C运行时库DLL(s)与您的应用程序一起分发,则可以考虑静态链接到CRT。 这将代码嵌入到您的可执行文件中,并消除了单独的DLL的要求。 同样,这会使您的可执行文件变得更加庞大,但是不需要安装程序甚至是ZIP文件就可以更简单地进行部署。 当然,最重要的警告就是你无法获得对CRT DLL的增量安全更新。 你必须重新编译和重新分配应用程序来获得这些修复。 对于没有其他依赖的玩具应用程序,我经常选择静态链接; 否则,动态链接仍然是推荐的方案。

NtDll中有一些C运行库函数。 根据Windows内部,这些仅限于字符串操作功能。 还有其他的等价物,比如使用HeapAlloc而不是malloc,所以你可以根据你的需求摆脱它。

尽管这些功能已经被微软的出版物所认可,并且已经被内核程序员使用了很多年,但是它们并不是官方的Windows API的一部分,除了玩具或者演示程序以外,它们不应该用于其它任何东西,因为它们的存在和功能可能更改。

你可能想阅读这里为Rust语言做的这个选项的讨论。

这是否意味着我可以在运行时通过LoadLibrary和GetProcAddress动态调用它们?

是。 甚至更多 – 为什么不使用ntdll.lib(或ntdllp.lib)静态绑定到ntdll? 在这之后你可以直接调用这个函数而不用任何GetProcAddress

这是保证是每个Windows版本的情况下?

从nt4到win10在ntdll中存在很多C运行库函数,但是它们的设置是不同的。 通常它从版本到版本增长。 但有些功能比较少,比较msvcrt.dll。 比如说ntdll的printf不支持浮点格式,但是一般功能是一样的

有可能完全删除C运行时库(仅使用NtDll的CRT函数),因此使我的程序更小?

是的,这是100%可能的。