用内存查看工具vmmap来理解HINSTANCE和HMODULE的具体含义

做win32开发以来,HINSTANCE和HMODULE用到的地方很多,一般而言,HINSTANCE指的是跟exe相关的,HMODULE跟dll相关。
比如win32界面程序,

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_SIMPLEMESSAGE, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);
    ...
}

此段代码中,wWinMain函数的第一个参数就是HINSTANCE,代表当前进程exe的实例句柄。

而关于HMODULE的,最常见的是下面的dll动态加载,返回的是dll的实例句柄。

HMODULE WINAPI LoadLibrary(
  _In_ LPCTSTR lpFileName
);

无论是exe,还是dll,上面讲的实例句柄都比较抽象,能否以感官认知,来进行理解,为此我此处用到了内存查看工具VMMap。
首先,我们来用win32窗口例子来说明,此处我用的是vs2015,固定住exe在虚拟内存中的加载地址,如下所示:
在这里插入图片描述
红色笔圈住的地方,就是exe在进程中的映射地址,此处填写的是0x00df0000,关于如何固定住exe基址,大家可以网上搜索。然后运行此exe,出现的是一个窗体界面。我们用vmmap来查看此进程的虚拟地址空间。

在这里插入图片描述
我们直接找0x00df0000打头的地址,可以看到,对应的模块就是exe本身。
并且还可以看出,此exe本身占用了多大的虚拟内存空间,在展开的若干行中,列举了此exe内部的若干区段,比如Header段,指的就是头部信息所在的段,占用了4K的大小空间,.text段是代码段,也占用了4K的大小空间,最后一个.rsrc,资源区段,占用了96K的空间,可以看出,对于界面程序而言,一般资源占用的空间最大。

通过上面的例子,可以看出,HINSTANCE其实就是模块exe加载到进程虚拟地址空间后的映射地址。

那么HMODULE对应的dll,是不是也是一样的呢,我们编写一个例子看看。
此处编写一个求两个正整数的最大公约数的例子,dll中导出函数,如下所示:
extern “C” __declspec(dllexport) int _stdcall Gcd(int a, int b);
Gcd就表示求a和b的最大公约数。对应的dll的名称叫做GcdDll.dll。
然后写一个exe(GcdExe.exe),动态加载此dll,代码如下:

#include "stdafx.h"
#include <stdio.h>
#include <Windows.h>

typedef int (CALLBACK *GCD)(int, int);

int main()
{
	int a = 150;
	int b = 200;

	HMODULE hModule = LoadLibraryA("GcdDll.dll");
	printf("hModule is %p\n", hModule);
	GCD gcd = (GCD)GetProcAddress(hModule, "Gcd");
	printf("address of Gcd is %p\n", gcd);
	int c = gcd(a, b);
	printf("the common division of %d and %d is %d\n", a, b, c);

	while (1);
    return 0;
}

运行结果如下所示:
在这里插入图片描述
可以看出,dll加载到进程虚拟地址空间的地址是0x66B50000,而导出函数Gcd在进程虚拟地址空间的地址是0x66B51010。
下面我们来用内存查看工具查看进程GcdExe.exe的地址空间
在这里插入图片描述
可以看出,此dll映射到进程的地址空间的地址就是0x66B50000,而Gcd的地址0x66B51010是怎么来的呢,我们可以借助depends工具,查看GcdDll.dll,如下所示:
在这里插入图片描述
我们查看右边的导出函数Gcd所在的条目,里面有个Entry Point,值是0x00001010,此值是此函数相对于此dll的偏移地址。
所以Gcd函数在进程地址空间的地址0x66B51010,就是Gcd函数所在模块GcdDll.dll的映射地址0x66B50000加上Gcd函数在dll内的偏移地址0x00001010。

顺带说下,exe和dll在我看来没什么不同,LoadLibrary也可以加载exe。
关于内存查看工具vmmap.exe,本来想上传资源的,结果发现已经有人上传,我上传不了,大家自己找吧。

猜你喜欢

转载自blog.csdn.net/tusong86/article/details/106972638