函数链接与调用,导入函数的调用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18218335/article/details/79166079

环境:VS2017 Community,Win32 Debug,项目属性,常规,全程序优化,无全程序优化

首先来看普通函数的调用过程

#include <stdio.h>
void hello()
{
    printf("Hello World\r\n");
    return;
}
int main()
{
    hello();

    getchar();
    getchar();
    return 0;
}

来看反汇编:
这里写图片描述

通常我们都会在这里F10进入函数,今天我们选择F11单步执行如下:
这里写图片描述

注意,程序跳转到了一个位置,从反汇编来看,此区域是一个比较大的跳转表,而且这个跳转表是在exe 模块内部的,其上下的跳转表对应的都是XXXcrtXXXX函数,即运行时函数,由此可见,这个表跳转是函数内部的一个跳转表。

为什么函数调用要通过这种看似间接的方式进行?

《老码识途》作者给出的解释是:PE (Windows 平台下)的生成通常包括两步:编译,链接。编译针对的是.cpp/.c 文件,产物是.obj 文件。当源代码存在多个模块(.cpp/.c文件)时,模块之间(.cpp/.c文件)可能会有函数调用关系,为了方便的引用模块之间的函数,每个obj 文件都有两个列表,一个列表,包含了本模块的所有函数,另一个列表,包含了本模块所引用的尚未确定的函数。链接前,obj文件中的函数调用是通过call offset + jmp 地址来进行函数调用的,如果所调用的函数尚未确定,此时call(E8 offset) 后面的地址为0。链接时,链接器一次解决所有模块中尚未确定的模块的地址,并将E8指令后的偏移修正为正确的值。

当所调用的函数为导入函数的时候是什么样的情况呢?

构建如下的DLL:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"


#include <stdio.h>
#include <windows.h>
extern "C" __declspec(dllexport) void hello()
{
    printf("Hello World\r\n");
    return;
}
BOOL APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        hello();
        break;
    case DLL_PROCESS_DETACH:

        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    }

    return TRUE;
}

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

上图分别是Dll 内部自己调用hello 函数,与exe 调用导入的hello 函数的过程。我们可以看到,当dll内部,即pe 调用内部的函数时将会使用e8+偏移指令,然后跳转到一个jmp 指令,jmp 跳转到真正的位置执行我们的函数。

当exe 调用导入的函数时,首先通过一个ff 15 [地址]指令,跳转到导入表中指定的地址,然后再执行jmp 指令跳转到真正的函数实现的地址。通过对比我们发现,该jmp 指令的地址是相同的,且该jmp 指令在dll 模块中。

由此我们可以总结得到函数调用的过程为:
call + jmp

当我们使用GetProcAddress 和 函数指针时,得到的函数地址是什么格式的?

这里写图片描述

这里写图片描述

FF 55 EC(取反加一即为-14call dword ptr [ebp-14h]

同样是通过读取内存来进行第一次的跳转,而这个内存为栈内存
这里写图片描述

同上。

&函数名为什么过程???

这里写图片描述

同上

猜你喜欢

转载自blog.csdn.net/qq_18218335/article/details/79166079