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

来源:互联网 发布:数据迁移工程师 编辑:程序博客网 时间:2024/06/02 05:12

环境:VS2012+WIN8 64

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

测试:VM WIN7

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

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


头文件 HelloWDM.h

#if __cplusplusextern "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 __cplusplusextern "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};//初始化 objectAttributesInitializeObjectAttributes(&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_PAGESPAGE_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_STRINGRtlInitAnsiString(&anFunName, functionName);//转化为UNICODE_STRINGRtlAnsiStringToUnicodeString(&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;}