如何从机器代码调用 Windows .DLL (API) 函数?

How can I call a Windows .DLL (API) function from machine code?

提问人:interstellarfrog 提问时间:11/6/2023 最后编辑:Sep Rolandinterstellarfrog 更新时间:11/6/2023 访问量:86

问:

我最近一直在做很多低级编码,并想尝试完全用机器代码编写一个程序来进一步了解我的知识(从 x64 汇编向下翻译,并将其添加到 PE 文件的正确部分)

我遇到的唯一问题是我在 Windows 上,我必须使用 win API 而不是系统调用来拥有我可以与之交互的任何内容,例如窗口甚至只是 stdout,显然没有很多关于如何从机器代码调用 .DLL 函数的指南。我假设它可能是由操作系统使用 PE 文件中的某些字段完成的,而不是实际上由编译器完成的。有人有这方面的信息吗?

Windows Assembly DLL x86-64 机器代码

评论

2赞 vitsoft 11/6/2023
在 PE 文件中,您至少需要另外两个手工制作的部分,由可选标头数据目录标识为导入表IAT
4赞 David Wohlferd 11/6/2023
试这个。在链接答案的底部是一个工作示例。请注意,此答案不使用链接器。它使用 nasm 直接写入 exe 的能力。

答:

3赞 Joshua 11/6/2023 #1

我在这里的实际示例: https://github.com/joshudson/dossuperfloppy/blob/660586f2bb3d660d8154b0cb6857562dc4892bcf/postntfs.asm#L374 32 位和 64 位之间的差异足够小,这仍然是该部分应该是什么样子的一个很好的例子。.idata

你需要发出这个部分(是的,它可以与另一个部分结合,但内容需要在那里.......idata

在 PE 标头中,第二个 RVA 指针指向导入目录,而第 13 个指针指向导入地址表(稍后会详细介绍)。

导入表中的每个条目都由五个单词组成。第一个 dword 是此 dll 启动的 IA 表中位置的 RVA。在最简单的情况下,第二个和第三个将为零。第四个是 DLL 名称的 ASCIIZ 字符串的 RVA(不,不能在 unicode 名称中导入 dll),第五个是导入地址转换表的 RVA。该表以 5 个零字的条目结束。(为什么它以这种方式结束,而不是在标题中使用 size 参数,我不知道;请注意,RVA 表中的大小包括终止条目。导入表必须与 dword 对齐。

导入地址表由进入导入名称表的 machine-word(dword 表示 32 位,qword 表示 64 位)RVA 指针组成。零将停止处理此特定导入的 DLL。此表必须与机器字对齐。

import names 表由包含单词提示(如果图像尚未绑定,则 0 是提示值的最佳选择)的条目组成,后跟一个 ASCIIZ 字符串,该字符串提供要导入的符号的名称。名称表中的每个条目都必须是单词对齐的。如果名称表中的条目无法对齐,则会导致一个特别令人困惑的错误。

在磁盘映像中,已转换的导入地址 (IAT) 表是导入地址表的副本。在加载过程中,它被指向实际导入符号的指针覆盖。因此,该部分被宣布为可写。.idata

使用导入符号的最简单方法是发出指令。由于您打算在机器代码中执行此操作的痛苦,因此发出的指令被编码为 FF 15,后跟一个小词尾,即(六字节)指令末尾与导入地址转换表中的条目之间的字节数(在 RAM 中加载;请注意,这很可能不是图像中的距离)。call [rel iat-offset]

RVA:相对虚拟地址。一个指针,其基址是内存中图像的加载地址。