Windows 符号的语义

Windows 符号的语义,windows,dll,linker,reverse-engineering,portable-executable,Windows,Dll,Linker,Reverse Engineering,Portable Executable,调用DLL函数涉及链接器生成存根,除非函数被声明为\uu declspec(dllimport),在这种情况下,存根可以被绕过,而直接调用导入表,这样效率会稍微高一些,例如 __declspec(dllimport) void ExitProcess(int); ExitProcess(0); 产生 call qword ptr [__imp_ExitProcess] 其中,\uuu imp\u ExitProcess解析为可执行文件的导入表中的一个位置 我试图弄清楚\uu imp\u Ex

调用DLL函数涉及链接器生成存根,除非函数被声明为
\uu declspec(dllimport)
,在这种情况下,存根可以被绕过,而直接调用导入表,这样效率会稍微高一些,例如

__declspec(dllimport) void ExitProcess(int);
ExitProcess(0);
产生

call qword ptr [__imp_ExitProcess]
其中,
\uuu imp\u ExitProcess
解析为可执行文件的导入表中的一个位置

我试图弄清楚
\uu imp\u ExitProcess
是如何解决的。可以肯定,它在kernel32.lib中作为一个符号出现,但该符号具有存储类
IMAGE\u SYM\u class\u EXTERNAL
、节号零和值零,这相当于只说“这将在其他地方定义”,而没有实际定义它


\uuuu imp\uuu
前缀是否具有特殊含义,即链接器是否注意到该前缀,并将其作为一条指令,用于将符号解析为名称已删除该前缀的DLL函数的导入表项?还是发生了其他事情?

内核32.lib的成员不是普通的对象文件,而是特殊的占位符。从:

传统的导入库,即描述 从一个图像导出供另一个图像使用,通常遵循布局 如第7节“存档(库)文件格式”所述 不同之处在于导入库成员包含伪对象文件 而不是真实的,每个成员都包含节 构建以下导入表所需的贡献: 如第6.4节“idata部分”所述。链接器生成 在生成导出应用程序时创建此存档

可以从一小部分信息推断导入的节贡献。链接器可以生成完整的, 在导入库中输入每个成员的详细信息 库的创建时间或仅写入规范信息的时间 并让以后使用它的应用程序生成 必要的动态数据

[…]一个简短的导入库编写如下:

  Archive member header   
  Import header
  Null-terminated import name string
  Null-terminated DLL name string
这是足够的信息,可以在使用构件时准确地重建构件的整个内容

在我的机器上的
kernel32.lib
中,第一个和第二个链接器成员(符号列表)中提到了
\uu imp\u ExitProcess
,并指向描述导入的特定伪对象:

Archive member name at 8: /               
50107C36 time/date Thu Jul 26 01:07:34 2012
         uid
         gid
       0 mode
   106CA size
correct header end

    2515 public symbols

    [...]

    3C874 ExitProcess
    3C874 __imp_ExitProcess

    [...]

Archive member name at 3C874: KERNEL32.dll/   
50107639 time/date Thu Jul 26 00:42:01 2012
         uid
         gid
       0 mode
      2D size
correct header end

  Version      : 0
  Machine      : 8664 (x64)
  TimeDateStamp: 50107639 Thu Jul 26 00:42:01 2012
  SizeOfData   : 00000019
  DLL name     : KERNEL32.dll
  Symbol name  : ExitProcess
  Type         : code
  Name type    : name
  Hint         : 371
  Name         : ExitProcess
因此,如您所见,.lib中的数据明确表示它引用了DLL
KERNEL32.DLL
中的导入名称
ExitProcess
。链接器可以使用它在导入部分构建必要的元数据


现在,上面只讨论了
\uu imp\u ExitProcess
符号是如何解析的。我不是100%确定,但我认为如果一个符号(例如,
ExitProcess
)已解析为这样一个导入存根,并且它不是以
\uuu imp\uu
开始的,那么链接器必须生成一个跳转存根(用于代码符号)或间接访问(用于数据访问)IAT插槽。

刚刚提出了一个测试,给出了一个数据点。以下节目:

__declspec(dllimport) void ExitProcess(int);

void __imp_ExitProcess(int x) {
}

int main() {
    ExitProcess(0);
}

使用Microsoft编译器编译并运行时崩溃(但如果将空函数重命名为其他任何函数,则运行正常);因此,链接器的行为似乎就好像
\uuu imp\uu
名称是特殊的,至少在某种意义上,以这种方式运行的链接器将在Microsoft链接器执行此操作的所有情况下生成正确的可执行文件,除非我遗漏了某些内容。

链接器向程序中添加了一个函数,如:

void (*__imp_ExitProcess)(int) = ...;

void ExitProcess(int n)
{
    return (*__imp_ExitProcess)(n);
}
其中uu imp_ExitProcess指向KERNEL32.DLL中的“真实”ExitProcess

在代码中声明:

__declspec(dllimport) void ExitProcess(int);
相当于:

extern void (*__imp_ExitProcess)(int);

#define ExitProcess (*__imp_ExitProcess)

除了IgorSkochinsky编译的不是由预处理器处理的dxLimeStf.d/P> < <代码> Keln3L.Lb<代码>,你是否看到@ @ 64位版本的微软C++ 2013。286522字节。它是一个外部文件,在kernel32.dll中的其他地方定义。这在运行时由加载程序解决。前缀没有意义。@HansPassant对,运行时加载程序从导入表开始工作,不关心前缀。但是在链接时,链接器如何知道
\uu imp\u ExitProcess
应该指向导入表,而不是作为未定义的符号被拒绝?您在最后一段的第二段中所说的是正确的,我对存根生成的理解也与您在最后一段中所说的一致。然而,我仍然不清楚
\uu imp\u ExitProcess
是如何解决的。可以肯定的是,符号列表中数字的一致性暗示它在某种程度上与
ExitProcess
有关,但这如何转化为解析符号的规范或算法呢?我的第一个猜想是,它是通过去掉前面的
\uuuuu imp\uuu
来工作的……从符号列表中可以推测,它可能会沿着“当一个外部符号具有与显式导入相同的符号列表编号,并且它们是仅有的两个具有该编号的符号时,以这种方式将它们连接起来,“但我不知道是这样还是其他原因——如果是这样,如果有几个符号具有相同的数字会发生什么?这是第一个确定的链接器成员吗?即使说明书上说一个已经过时了?Etc.@rwallace:阅读PE/COFF规范,特别是关于归档文件的第一个和第二个成员(符号索引)的部分。使用索引查找定义符号的存档成员。在这种情况下,存档成员不是一个完整的对象文件,而是一个导入存根,因此您不需要执行任何额外的符号查找-只需使用其中的信息来生成导入目录元数据。是的,我已经阅读了该节十到十五次,尽管我可能仍然缺少一些含义。但你认为他不会