内核分析PE获取DLL导出函数地址

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

环境:VS2012+WIN8 64

类型:C++编写的WDM驱动程序

测试:VM WIN7

用途:主要用于驱动程序中得到WIN32 API地址,也可得到自定义的DLL中的函数导出地址,记录内核文件相关操作以便以后查看。

说明:此段代码来源于网络,经修改调试而成。


头文件 HelloWDM.h

#if __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#include <windef.h>
#ifdef __cplusplus
}
#endif

//winnt.h中的定义 由于是WDM不能引用该文件 所以只有复制过来
#define SEC_IMAGE         0x1000000  

//PE相关结构
typedef struct _SECTION_IMAGE_INFORMATION
{
     PVOID TransferAddress;
     ULONG ZeroBits;
     ULONG MaximumStackSize;
     ULONG CommittedStackSize;
     ULONG SubSystemType;
     union
     {
          struct
          {
               WORD SubSystemMinorVersion;
               WORD SubSystemMajorVersion;
          };
          ULONG SubSystemVersion;
     };
     ULONG GpValue;
     WORD ImageCharacteristics;
     WORD DllCharacteristics;
     WORD Machine;
     UCHAR ImageContainsCode;
     UCHAR ImageFlags;
     ULONG ComPlusNativeReady: 1;
     ULONG ComPlusILOnly: 1;
     ULONG ImageDynamicallyRelocated: 1;
     ULONG Reserved: 5;
     ULONG LoaderFlags;
     ULONG ImageFileSize;
     ULONG CheckSum;
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

HelloWDM.cpp文件

#include "HelloWDM.h"

//得到DLL中的指定函数地址 相当于应用层的GetProcAddress函数
DWORD GetDllFunctionAddress(PTSTR lpFunctionName, PTSTR pDllName)
{
    HANDLE hThread, hSection, hFile, hMod;
    SIZE_T size=0;
    NTSTATUS status;
    PVOID BaseAddress = NULL;

    //转换DLL名称
    UNICODE_STRING strDllName;
    RtlInitUnicodeString(&strDllName, pDllName);

    OBJECT_ATTRIBUTES objectAttributes={0};
    IO_STATUS_BLOCK iosb={0};

    //初始化 objectAttributes
    InitializeObjectAttributes(&objectAttributes, &strDllName, OBJ_KERNEL_HANDLE, NULL, NULL);

    //打开文件
    status=ZwOpenFile(&hFile,  FILE_EXECUTE | SYNCHRONIZE, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
    if(!NT_SUCCESS(status))
    {
        return status;
    }
    objectAttributes.ObjectName = 0;

    //创建内存块
    status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &objectAttributes, 0, PAGE_READONLY, SEC_IMAGE, hFile); //PAGE_READONLY页面保护属性,必须结合SEC_IMAGE属性
    if(!NT_SUCCESS(status))
    {
        return status;
    }

    //内存映射文件
    status=ZwMapViewOfSection(hSection, 
                            ZwCurrentProcess(), 
                            &BaseAddress, 
                            0, 
                            1024, 
                            0, 
                            &size, 
                            ViewUnmap, 
                            MEM_LARGE_PAGES,        //针对DLL文件较小是可以用MEM_TOP_DOWN 文件较大比如USER32.DLL时需要用MEM_LARGE_PAGES
                            PAGE_READWRITE);        
    if(!NT_SUCCESS(status))
    {
        return status;
    }
    //关闭文件句柄
    ZwClose(hFile);

    //读取PE头信息
    IMAGE_DOS_HEADER* dosheader;
    IMAGE_OPTIONAL_HEADER* opthdr;
    IMAGE_EXPORT_DIRECTORY* pExportTable;
    PDWORD arrayOfFunctionAddresses, arrayOfFunctionNames;
    PWORD arrayOfFunctionOrdinals;
    DWORD functionOrdinal, functionAddress=0;
    PSTR functionName;
    ANSI_STRING anFunName;
    UNICODE_STRING unFunctionName, unFunctionNameSearch;
    //模块句柄
    hMod = BaseAddress;
    //得到DOS头
    dosheader = (PIMAGE_DOS_HEADER)hMod;
    //得到PE选项头
    opthdr =(PIMAGE_OPTIONAL_HEADER) ((PBYTE)hMod+dosheader->e_lfanew+24);
    //得到导出表
    pExportTable =(PIMAGE_EXPORT_DIRECTORY)((PBYTE) hMod + opthdr->DataDirectory[0].VirtualAddress);
    //得到函数地址列表
    arrayOfFunctionAddresses = (PDWORD)( (PBYTE)hMod + pExportTable->AddressOfFunctions);
    //得到函数名称列表
    arrayOfFunctionNames = (PDWORD)( (PBYTE)hMod + pExportTable->AddressOfNames);
    //得到函数序号
    arrayOfFunctionOrdinals = (PWORD)( (PBYTE)hMod + pExportTable->AddressOfNameOrdinals);
    //导出表基地址
    DWORD Base = pExportTable->Base;

    //转换函数名
    RtlInitUnicodeString(&unFunctionNameSearch, lpFunctionName);
    //循环导出表
    for(DWORD x = 0; x < pExportTable->NumberOfNames; x++)            //导出函数有名称 编号之分,导出函数总数=名称导出+编号导出,这里是循环导出名称的函数
    {
        //得到函数名 
        functionName = (PSTR)( (PBYTE)hMod + arrayOfFunctionNames[x]);

        //转化为ANSI_STRING
        RtlInitAnsiString(&anFunName, functionName);
        //转化为UNICODE_STRING
        RtlAnsiStringToUnicodeString(&unFunctionName, &anFunName, TRUE);
        //打印调试信息
        KdPrint(("%d/%d,FunName:%wZ\n", x+1, pExportTable->NumberOfNames, &unFunctionName));
        //比较函数名称
        if (RtlCompareUnicodeString(&unFunctionName, &unFunctionNameSearch, TRUE) == 0)
        {
            //得到该函数地址
            functionOrdinal = arrayOfFunctionOrdinals[x] + Base - 1;
            functionAddress = (DWORD)( (PBYTE)hMod + arrayOfFunctionAddresses[functionOrdinal]);
            break;
        }
    }

    ZwClose(hSection);

    return functionAddress;
}

以上代码虽可以运行但没有考虑到 ZwMapViewOfSection的资源释放问题 修改如下:

//HelloWDM.h

#if __cplusplus
extern "C"
{
#endif
#include <wdm.h>
#include <windef.h>
#ifdef __cplusplus
}
#endif

//定义设备扩展
typedef struct _DEVICE_EXTERSION
{
    PDEVICE_OBJECT fdo;
    PDEVICE_OBJECT NextStatckDevice;
    UNICODE_STRING ustrDeviceName;        //设备名
    UNICODE_STRING ustrSymLinkName;        //符号链接名
    PVOID tmpPoint;                        //记录临时指针
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//全局变量 
PDEVICE_EXTENSION gDevExt=NULL;

//winnt.h中的定义 由于是WDM不能引用该文件 所以只有复制过来
#define SEC_IMAGE         0x1000000  

//PE相关结构
typedef struct _SECTION_IMAGE_INFORMATION
{
     PVOID TransferAddress;
     ULONG ZeroBits;
     ULONG MaximumStackSize;
     ULONG CommittedStackSize;
     ULONG SubSystemType;
     union
     {
          struct
          {
               WORD SubSystemMinorVersion;
               WORD SubSystemMajorVersion;
          };
          ULONG SubSystemVersion;
     };
     ULONG GpValue;
     WORD ImageCharacteristics;
     WORD DllCharacteristics;
     WORD Machine;
     UCHAR ImageContainsCode;
     UCHAR ImageFlags;
     ULONG ComPlusNativeReady: 1;
     ULONG ComPlusILOnly: 1;
     ULONG ImageDynamicallyRelocated: 1;
     ULONG Reserved: 5;
     ULONG LoaderFlags;
     ULONG ImageFileSize;
     ULONG CheckSum;
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

//HelloWDM.cpp

//得到DLL中的指定函数地址 相当于应用层的GetProcAddress函数
DWORD GetDllFunctionAddress(PTSTR lpFunctionName, PTSTR pDllName)
{
	HANDLE hSection=NULL, hFile=NULL;
	SIZE_T size=0;
	NTSTATUS status;
	PVOID BaseAddress = NULL;

	//转换DLL名称
	UNICODE_STRING strDllName;
	RtlInitUnicodeString(&strDllName, pDllName);

	OBJECT_ATTRIBUTES objectAttributes={0};
	IO_STATUS_BLOCK iosb={0};

	//初始化 objectAttributes
	InitializeObjectAttributes(&objectAttributes, &strDllName, OBJ_KERNEL_HANDLE, NULL, NULL);

	__try
	{
		//打开文件
		status=ZwOpenFile(&hFile,  FILE_EXECUTE | SYNCHRONIZE, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
		if(!NT_SUCCESS(status))
		{
			__leave;
		}
		objectAttributes.ObjectName = 0;

		//创建内存块
		status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &objectAttributes, 0, PAGE_READONLY, SEC_IMAGE, hFile); //PAGE_READONLY页面保护属性,必须结合SEC_IMAGE属性
		if(!NT_SUCCESS(status))
		{
			__leave;
		}

		//内存映射文件
		status=ZwMapViewOfSection(hSection, 
			ZwCurrentProcess(), 
			&BaseAddress, 
			0, 
			1024, 
			0, 
			&size, 
			ViewUnmap, 
			MEM_LARGE_PAGES,		//针对DLL文件较小是可以用MEM_TOP_DOWN 文件较大比如USER32.DLL时需要用MEM_LARGE_PAGES
			PAGE_READWRITE);		
	}
	__finally
	{
		if(hFile != NULL)
		{
			//关闭文件句柄
			ZwClose(hFile);		
		}
		if(!NT_SUCCESS(status) && hSection != NULL)
		{
			//关闭内存块
			ZwClose(hSection);
		}
	}
	//如果失败 直接返回
	if(!NT_SUCCESS(status))
	{
		return 0;
	}

	//读取PE头信息
	IMAGE_DOS_HEADER* dosheader;
	IMAGE_OPTIONAL_HEADER* opthdr;
	IMAGE_EXPORT_DIRECTORY* pExportTable;
	PDWORD arrayOfFunctionAddresses, arrayOfFunctionNames;
	PWORD arrayOfFunctionOrdinals;
	DWORD functionOrdinal, functionAddress=0;
	PSTR functionName;
	ANSI_STRING anFunName;
	UNICODE_STRING unFunctionName, unFunctionNameSearch;
	//模块句柄
	HANDLE hMod = BaseAddress;
	//得到DOS头
	dosheader = (PIMAGE_DOS_HEADER)hMod;
	//得到PE选项头
	opthdr =(PIMAGE_OPTIONAL_HEADER) ((PBYTE)hMod+dosheader->e_lfanew+24);
	//得到导出表
	pExportTable =(PIMAGE_EXPORT_DIRECTORY)((PBYTE) hMod + opthdr->DataDirectory[0].VirtualAddress);
	//得到函数地址列表
	arrayOfFunctionAddresses = (PDWORD)( (PBYTE)hMod + pExportTable->AddressOfFunctions);
	//得到函数名称列表
	arrayOfFunctionNames = (PDWORD)( (PBYTE)hMod + pExportTable->AddressOfNames);
	//得到函数序号
	arrayOfFunctionOrdinals = (PWORD)( (PBYTE)hMod + pExportTable->AddressOfNameOrdinals);
	//导出表基地址
	DWORD Base = pExportTable->Base;

	//转换函数名
	RtlInitUnicodeString(&unFunctionNameSearch, lpFunctionName);
	//循环导出表
	for(DWORD x = 0; x < pExportTable->NumberOfNames; x++)			//导出函数有名称 编号之分,导出函数总数=名称导出+编号导出,这里是循环导出名称的函数
	{
		//得到函数名 
		functionName = (PSTR)( (PBYTE)hMod + arrayOfFunctionNames[x]);

		//转化为ANSI_STRING
		RtlInitAnsiString(&anFunName, functionName);
		//转化为UNICODE_STRING
		RtlAnsiStringToUnicodeString(&unFunctionName, &anFunName, TRUE);
		//打印调试信息
		KdPrint(("%d/%d,FunName:%wZ\n", x+1, pExportTable->NumberOfNames, &unFunctionName));
		//比较函数名称
		if (RtlCompareUnicodeString(&unFunctionName, &unFunctionNameSearch, TRUE) == 0)
		{
			//得到该函数地址
			functionOrdinal = arrayOfFunctionOrdinals[x] + Base - 1;
			functionAddress = (DWORD)( (PBYTE)hMod + arrayOfFunctionAddresses[functionOrdinal]);
			break;
		}
	}
	//这里释放资源返回的地址将无效 所以先存放起来
	//ZwUnmapViewOfSection (NtCurrentProcess(), BaseAddress);
	gDevExt->tmpPoint=BaseAddress;

	ZwClose(hSection);

	return functionAddress;
}

调用代码如下:

ULONG ulOriginalProcAddr=GetDllFunctionAddress(TEXT("NtOpenProcess"), TEXT("\\SystemRoot\\system32\\ntdll.dll"));
//释放GetDllFunctionAddress的内存块
if(gDevExt->tmpPoint!=0)
{
    ZwUnmapViewOfSection (NtCurrentProcess(), gDevExt->tmpPoint);
    gDevExt->tmpPoint=0;
}




 

猜你喜欢

转载自blog.csdn.net/whatday/article/details/9879335