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

环境: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; } 




 

你可能感兴趣的:(内核分析PE获取DLL导出函数地址)