核心分析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;
}
相關文章
- C++ 獲取指定的過載函式地址C++函式
- qt 打包釋出 獲取dllQT
- C++關於DLL匯出模板類和模板函式C++函式
- dll的def檔案與__declspec(dllexport)匯出函式方式比較Export函式
- [轉]根據PE檔案格式獲取LoadLibraryA()/GetProcAddress()地址
- dll 入口函式函式
- PostgreSQL 函式獲取表DDLSQL函式
- GetDiskSerial DLL獲取硬碟資訊硬碟
- 獲取URL地址
- .Net CLR GC動態獲取函式頭地址,C++的騷操作(慎入)GC函式C++
- php 獲取函式被呼叫位置PHP函式
- saltstack獲取IP地址
- 獲取IP地址方法
- 獲取IP地址命令
- ASPNET獲取IP地址 MAC地址Mac
- java獲取ip地址和mac地址JavaMac
- 函式名/函式地址/函式指標函式指標
- C# 呼叫dll獲取dll物理路徑的方法C#
- C語言getgroups()函式:獲取組程式碼函式C語言函式
- 視窗屬性的獲取函式函式
- 用前面建立的函式獲取物件函式物件
- DLL庫的編寫(匯出、匯入)與使用
- 核心堆分配函式brk()原始碼分析函式原始碼
- PE結構體中匯出表/匯入表解析——初階結構體
- Java 中獲取MAC地址 和IP地址JavaMac
- 美國ip地址如何獲取?
- 獲取配置的mock地址Mock
- 獲取真實IP地址
- shell指令碼獲取函式返回值指令碼函式
- 獲取javascript函式形參的數目JavaScript函式
- 反射如何獲取函式的引數名反射函式
- sql 獲取系統時間的函式。SQL函式
- 如何獲取海外住宅IP地址?
- 獲取客戶端Mac地址客戶端Mac
- jQuery獲取本機ip地址jQuery
- php怎麼獲取mac地址?PHPMac
- Java獲取本機ip地址Java
- Js獲取URL地址引數JS