【问题标题】:Compile a C++ program with only dependency on kernel32.dll and user32.dll?编译只依赖 kernel32.dll 和 user32.dll 的 C++ 程序?
【发布时间】:2011-01-24 19:33:11
【问题描述】:

我正在使用 Visual Studio 2005。

我想编译一个简单的程序,它可以在任何 Windows 32 位版本上工作,而与安装的 c++ 运行时库版本无关。

此程序将调用GetModuleHandleGetProcAddress 函数而不进行任何其他函数调用,然后退出,当退出代码为函数地址时。

如何编译一个只依赖kernel32.dll和user32.dll的C++程序,没有任何c++运行时库?

【问题讨论】:

  • +1 这是一个很好的 SO 问题的缩影。
  • 如果我可以问,这个程序的目的是什么?
  • 我正在实现从 64 位进程到 32 位进程的代码注入,我需要帮助 32 位程序来查找某些函数的地址。
  • 也许你应该问这个问题,因为有比创建帮助程序更好的方法。在我自己的开源项目(查看我的个人资料)中,我有 64 位到 32 位的 DLL 注入,为此我需要检索 32 位 LoadLibrary 的地址。您所要做的就是自己映射到 DLL 中并查找函数地址,然后添加远程进程加载的 DLL 的基地址。

标签: c++ windows visual-c++ runtime


【解决方案1】:

您需要定义自己的入口点,而不是使用mainWinMain。您的入口点是一个不带参数的 void 函数。您必须使用/entry:funcName 将其名称指定给链接器(其中funcName 将替换为您为要用作入口点的函数提供的任何名称)。

执行此操作时,您还必须为链接器指定子系统,如/subsystem:console。它通常根据找到的函数名称(即main -> 控制台,WinMain -> Windows)推断子系统,但是当您使用自己的入口点时,您必须明确指定它。尽管您可能不想经常这样做,但即使您没有指定自己的入口点,您也可以显式指定子系统,因此(例如)您可以使用 main 作为 windows 子系统程序的入口点,或WinMain 作为控制台程序的入口点。

【讨论】:

  • 另外,入口点必须声明为APIENTRY (== __stdcall),你需要使用ExitProcess()而不是从它返回。
  • @Brian:这不是 MSDN 所说的返回值 --msdn.microsoft.com/en-us/library/f9t8842e(v=VS.80).aspx
  • @Brian:从它返回工作正常。我通常也不用APIENTRY__stdcall 声明它——我只是用/Gz 编译。 OTOH,您的建议可能更好,即使它不是真的必要。
  • @Ben 和@Jerry :-) – 嗯,很有趣。刚刚在 Windows XP 上尝试过,如果你从入口点的末端掉下来,加载器确实会在你之后清理。当然,这并不令人惊讶。如果您没有明确调用ExitProcess(),我隐约记得早期版本的Windows 中发生的坏事,但我不太确定,而且我没有方便检查的旧系统。我宁愿怀疑这只是我一直在做的事情之一,只是因为我不知道更好......
  • @Brian:我很确定它“掉线”到最后至少可以正常工作到 NT 3.5。可能会导致 Windows 95 或 Win32S 之类的问题——我不确定。
【解决方案2】:

在您的项目选项下设置/NODEFAULTLIB。在较新版本的 Visual C++ 中,您还必须关闭堆栈溢出检查,因为这些会导致编译器自动插入对库函数的调用。

编辑:如果您的意思是“在任何 32 位 Windows 版本上运行”,您还必须使用 editbin 更改 PE 标头中的子系统版本字段。否则,在使用 VC++ 2005 链接器构建时,您将受限于 (IIRC) Windows 2000 及更高版本,并且较新版本的 VC++ 甚至更糟(默认情况下需要 XP)。 Windows 2000 是 5.0,您需要指定 3.5 或更高版本以允许除 Win9x 之外的所有 NT 版本。

【讨论】:

  • 虽然您确实想指定/nodefaultlib,但它本身只会给您unresolved external 错误。真正的关键是使用你自己的入口点来消除对库的依赖。
  • @Jerry:我想你可以根据链接器默认查找的任何内容(wCRTWinMain 或类似名称)命名你的入口点,但我同意使用/entry 更简洁。跨度>
  • 我从未尝试过,所以我想它可能有效,但不能肯定地说(没有测试,很难说链接器可能使用/调用/依赖什么魔法)。不管您是否使用/entry: 来指定它是什么,无论如何,您都必须定义入口点。
【解决方案3】:

我不知道为什么每个人都反对使用标准库。此方法假定您希望您的代码在 Windows 2000 或更高版本上运行,并且不介意失去对 Win 9x 的支持。您仍然可以使用 C/C++ 标准库 - 您可以在项目的 C/C++ 代码生成页面中使用 /MT 选项,该选项将静态链接到标准库中。

但是,有两个注意事项,第一个来自我:拥有一个动态链接的标准库的想法是,其中的任何错误都将被 Windows 更新修补(理论上)。如果静态链接库,则需要重新分发应用程序以修复标准库错误。所以不建议这样做。

其次,来自compiler options上的MSDN文章:

注意不要将静态和 运行时的动态版本 图书馆。拥有多于一份副本 进程中的运行时库 可能会导致问题,因为静态 一份副本中的数据不与 另一个副本。链接器防止 你从静态链接和 一个 .exe 文件中的动态版本, 但你仍然可以得到两个(或 更多)运行时的副本 图书馆。例如,动态链接 与静态链接的库 运行时的(非 DLL)版本 库在使用时可能会导致问题 带有链接的 .exe 文件 的动态(DLL)版本 运行时库。 (你还应该 避免混合调试和非调试 库的版本在一个 过程。)

简而言之,如果您尝试构建与动态链接标准库链接的其他组件,这样做可能会导致混乱。

当然,另一个缺点是这也会使您的可执行文件变得更大。

编辑:depends.exe 下的结果如下所示:(当然,我使用的是 64 位 Windows,它仅适用于 XP 及更高版本......如果你想知道它是什么样的在 32 位 Windows 上,想象一下 64s 是否不存在!)。

【讨论】:

  • “我不确定为什么每个人都反对使用标准库。”是的,这正是我的想法。 +1。
  • 我很高兴不只是我。我还尝试使用depends.exe profiler 来确保标准库不会被动态调用。以防万一。
  • 可能是因为标准库依赖于特定版本 Windows 中引入的函数,这与问题的要求相矛盾。而且问题还说“没有任何C++运行时库”。
  • 我可能是错的,也许我错了,但我认为他的意思是没有动态依赖说MSVCP80.DLL。标准库真的取决于您使用的 Windows 版本吗?
  • @Ninefingers:是的,VS2005标准库初始化代码调用了Win2000中引入的一些函数。如果您返回 Dependency Walker 并展开导入列表,您可能会找出哪个,GetProcessorFeature 或类似的东西,我已经忘记了(提示:Dependency Walker 在 MSDN 中有一个用于调用函数帮助的工具栏按钮,虽然我不知道MSDN是否还有比Win2000更远的信息)。
【解决方案4】:

检查微小的 c 库。也静态链接。

【讨论】:

  • 从技术上讲,这是一个答案,因为他提出的建议可能是 DxCK 开始/完成他的项目所需要的。但是,它可以与充实有关。
【解决方案5】:

您实际上也不需要 User32.dll,您真正无法删除的只有 Kernel32.dll 和 Ntdll.dll - 它们由 PsCreateProcess 注入您的进程空间(即内核创建一个新工艺)。

【讨论】:

  • 不,kernel32 没有被“PsCreateProcess”映射,顺便说一句,它不存在。 kernel32 在用户模式下由 ntdll 中的 LdrpInitializeProcess 加载。唯一不能删除的 DLL 是 ntdll。
  • 也许应该是 PspCreateProcess,这就是它“不存在”的原因。哦,好吧。
猜你喜欢
  • 1970-01-01
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2012-03-23
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多