内核分析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 headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG 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 imageDWORD AddressOfNames; // RVA from base of imageDWORD 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};//初始化 objectAttributesInitializeObjectAttributes(&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_PAGESPAGE_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_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;}}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 headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG 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 imageDWORD AddressOfNames; // RVA from base of imageDWORD 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;
}
内核分析PE获取DLL导出函数地址相关推荐
- C# 遍历DLL导出函数
C#如何去遍历一个由C++或E语言编写的本地DLL导出函数呢 不过在这里我建议对PE一无所知的人 你或许应先补补这方面的知识,我不知道为什么PE方面的 应用在C#中怎么这么少,我查阅过相关 C#的知识 ...
- windows 查看DLL导出函数的方法
windows 查看DLL导出函数的方法 点我查看原文 在window下查看动态库的导出函数可以用vs自带的Dependenc工具: 对于VC6.0,VC所带的Depends软件,在VC6安装目录下的 ...
- VC++ DLL 导出函数
VC++ DLL 导出函数 经常使用VC6的Dependency查看DLL导出函数的名字,会发现有DLL导出函数的名字有时大不相同,导致不同的原因大多是和编译DLL时候指定DLL导出函数的界定符有关 ...
- dll 导出函数 下划线_内核中的代码完整性:深入分析ci.dll
前言 在某些场景中,如果我们希望在允许某个进程进行特定动作前,以一种可靠的方式确认该进程是否可信,那么验证该进程的Authenticode签名是一个不错的方式.用户模式下的DLL wintrust提供 ...
- MFC DLL 导出函数的定义方式
一直在鼓捣DLL,每天的工作都是调试一个一个的DLL,往DLL里面添加自己的代码,但是对于DLL一直不太了解啊!今天一查资料,才发现自己对于DLL编写的一些基本知识也不了解.要学习,这篇文章先总结DL ...
- DLL导出函数名称改编的解决方法
*************************************************** 更多精彩,欢迎进入:http://shop115376623.taobao.com ****** ...
- windows查看dll导出函数名
1.找到vcvarsall.bat(我路径在D:\Program Files (x86)\Microsoft Visual Studio 10.0\VC),dos窗口中运行这个bat. 2.运行dum ...
- 获取上层调用函数地址的代码
_asm { add ebp,4 mov eax,[ebp] sub ebp,4 mov callerAdress,eax } #define CALLER_ADDRESS_STDCALL(x) / ...
- 【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取注入的 libbridge.so 动态库中的 load 函数地址 并 通过 远程调用 执行该函数 )
文章目录 一.dlsym 函数简介 二.获取 目标进程 linker 中的 dlsym 函数地址 三.远程调用 目标进程 linker 中的 dlsym 函数 获取 注入的 libbridge.so ...
最新文章
- 文本分类step by step(二)
- Spring.NET 1.3.1 新特性探索系列1——ASP.NET MVC的依赖注入
- python 只取数值_Python:你会生成列表吗?-- 列表生成式
- 并查集(Union-Find-Set)简洁而高效地处理连通分量的查询与合并
- C语言查找单链列表的第k个元素的算法(附完整源码)
- Consul在.Net Core中初体验
- linux中shell数组的使用和建立
- kvm虚拟化学习笔记(二)之linux kvm虚拟机安装
- python语言是 创造的_1.python简介
- matlab输入一个正的实数x,VB程序题:用InputBox 输入一个正实数,用Pring方法在一行上显示出它的平方和平方根、立方和立方根,每个数保留三位小数,其间有间隔。...
- SHA算法 (sha0 sha1源码魔改)
- linux CPU压力测试stress
- 开源一套DUI控件源码
- 用数据分析看共享单车
- windows 编译libtorrent
- java技术英文名词读音_Java开发,Java development,音标,读音,翻译,英文例句,英语词典...
- PyQt6 使用 QAxWidget 打开 IE/Word
- 苏教版四年级下册计算机说课稿,苏教版四年级下册认识多位数说课稿
- Win Server 2008 R2
- SQL列转行及行转列