GetProcAddress解析(一)
本帖最后由 元始天尊 于 2015-6-13 15:06 编辑剔除了很多无关代码
可以借鉴的地方:
1、考虑了很多极端情况如dllmain未执行,获得函数地址不能等于基址等
2、名称查找采用二分搜索
3、考虑了forward导出函数
4、对按序号和按名称分别处理
5、对内存pe解析
FARPROC WINAPI GetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
if (HIWORD(lpProcName) != 0)
{//按名称查找
RtlInitAnsiString(&ProcedureName, (LPSTR)lpProcName);
ProcNamePtr = &ProcedureName;
}
else
{ //按序号查找
Ordinal = (ULONG)lpProcName;
}
hMapped = BasepMapModuleHandle(hModule, FALSE);//如果没提供hModule则取得当前模块hModule
Status = LdrGetProcedureAddress(hMapped,ProcNamePtr, Ordinal,(PVOID*)&fnExp);
}
PVOID WINAPI BasepMapModuleHandle(HMODULE hModule, BOOLEAN AsDataFile)
{
if (!hModule) return NtCurrentPeb()->ImageBaseAddress;
if (LDR_IS_DATAFILE(hModule) && !AsDataFile)//如果为资源DLL
return NULL;
return hModule;
}
NTSTATUS NTAPI LdrpGetProcedureAddress(IN PVOID BaseAddress,IN PANSI_STRING Name,IN ULONG Ordinal,OUT PVOID *ProcedureAddress,IN BOOLEAN ExecuteInit)
{
NTSTATUS Status = STATUS_SUCCESS;
UCHAR ImportBuffer;
PLDR_DATA_TABLE_ENTRY LdrEntry;
IMAGE_THUNK_DATA Thunk;
PVOID ImageBase;
PIMAGE_IMPORT_BY_NAME ImportName = NULL;
PIMAGE_EXPORT_DIRECTORY ExportDir;
ULONG ExportDirSize, Length;
PLIST_ENTRY Entry;
if (Name)//若按名称查找
{
Length = Name->Length +sizeof(CHAR) + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name);
ImportName->Hint = 0;
RtlCopyMemory(ImportName->Name, Name->Buffer, Name->Length);
ImportName->Name = ANSI_NULL;
ImageBase = ImportName;
Thunk.u1.AddressOfData = 0;
}
else//若按序号查找
{
ImageBase = NULL;
Thunk.u1.Ordinal = Ordinal | IMAGE_ORDINAL_FLAG;
}
LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry); //查找已加载dll
ExportDir = RtlImageDirectoryEntryToData(LdrEntry->DllBase,TRUE,IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportDirSize);//查找导出表
Status = LdrpSnapThunk(LdrEntry->DllBase,ImageBase,&Thunk, &Thunk,ExportDir,ExportDirSize,FALSE,NULL);//获取导出表Thunk结构
*ProcedureAddress = (PVOID)Thunk.u1.Function;
}
BOOLEAN NTAPI LdrpCheckForLoadedDllHandle(IN PVOID Base,OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
{
PLDR_DATA_TABLE_ENTRY Current;
PLIST_ENTRY ListHead, Next;
if ((LdrpLoadedDllHandleCache) && (LdrpLoadedDllHandleCache->DllBase == Base))//查找缓存
{
*LdrEntry = LdrpLoadedDllHandleCache;
return TRUE;
}
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;//线性查找ldr
Next = ListHead->Flink;
while (Next != ListHead)
{
Current = CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);
if ((Current->InMemoryOrderModuleList.Flink) && (Base == Current->DllBase))
{
LdrpLoadedDllHandleCache = Current;
*LdrEntry = Current;
return TRUE;
}
Next = Next->Flink;
}
return FALSE;
}
PVOID NTAPI RtlImageDirectoryEntryToData(PVOID BaseAddress,BOOLEAN MappedAsImage,USHORT Directory,PULONG Size)
{
PIMAGE_NT_HEADERS NtHeader;
ULONG Va;
NtHeader = RtlImageNtHeader(BaseAddress);
if (NtHeader == NULL)
return NULL;
if(NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
return RtlpImageDirectoryEntryToData32(BaseAddress,MappedAsImage,Directory,Size,NtHeader);
else if(NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
return RtlpImageDirectoryEntryToData64(BaseAddress,MappedAsImage,Directory,Size,NtHeader);
return NULL;
}
PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(IN PVOID Base)
{
PIMAGE_NT_HEADERS NtHeaders = NULL;
PIMAGE_DOS_HEADER DosHeader;
if ((Base != NULL) && (Base == (PVOID)-1) && (DosHeader->e_magic == IMAGE_DOS_SIGNATURE))
{
if (DosHeader->e_lfanew < 0x10000000)
{
NtHeaders = (BYTE*)Base + DosHeader->e_lfanew;
if(NtHeaders->Signature != IMAGE_NT_SIGNATURE)
NtHeaders = NULL;
}
}
return NtHeaders;
}
PVOID NTAPI RtlpImageDirectoryEntryToData32(PVOID BaseAddress, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size, PIMAGE_NT_HEADERS32 NtHeader)
{
if(Directory >= NtHeader->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
DWORD vaaddr = NtHeader->OptionalHeader.DataDirectory.VirtualAddress;
if(vaaddr == 0)
return NULL;
*Size = NtHeader->OptionalHeader.DataDirectory.Size;
if(MappedAsImage || vaaddr < NtHeader->OptionalHeader.SizeOfHeaders)
return (PVOID)((BYTE*)BaseAddress + vaaddr);
}
PVOID NTAPI RtlpImageDirectoryEntryToData64(PVOID BaseAddress, BOOLEAN MappedAsImage, USHORT Directory, PULONG Size, PIMAGE_NT_HEADERS64 NtHeader)
{
if(Directory >= NtHeader->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
DWORD vaaddr = NtHeader->OptionalHeader.DataDirectory.VirtualAddress;
if(vaaddr == 0)
return NULL;
*Size = NtHeader->OptionalHeader.DataDirectory.Size;
if(MappedAsImage || vaaddr < NtHeader->OptionalHeader.SizeOfHeaders)
return (PVOID)((BYTE*)BaseAddress + vaaddr);
}
NTSTATUS NTAPI LdrpSnapThunk(IN PVOID ExportBase,IN PVOID ImportBase,IN PIMAGE_THUNK_DATA OriginalThunk,IN OUT PIMAGE_THUNK_DATA Thunk,
IN PIMAGE_EXPORT_DIRECTORY ExportEntry,IN ULONG ExportSize,IN BOOLEAN Static,IN LPSTR DllName)
{
BOOLEAN IsOrdinal;
USHORT Ordinal;
ULONG OriginalOrdinal = 0;
PIMAGE_IMPORT_BY_NAME AddressOfData;
PULONG NameTable;
PUSHORT OrdinalTable;
LPSTR ImportName = NULL;
USHORT Hint;
NTSTATUS Status;
ULONG_PTR HardErrorParameters;
UNICODE_STRING HardErrorDllName, HardErrorEntryPointName;
ANSI_STRING TempString;
ULONG Mask;
ULONG Response;
PULONG AddressOfFunctions;
UNICODE_STRING TempUString;
ANSI_STRING ForwarderName;
PANSI_STRING ForwardName;
PVOID ForwarderHandle;
ULONG ForwardOrdinal;
if ((IsOrdinal = IMAGE_SNAP_BY_ORDINAL(OriginalThunk->u1.Ordinal)))//按序号
{
OriginalOrdinal = IMAGE_ORDINAL(OriginalThunk->u1.Ordinal);
Ordinal = (USHORT)(OriginalOrdinal - ExportEntry->Base);
}
else
{
AddressOfData = (PIMAGE_IMPORT_BY_NAME)((ULONG_PTR)ImportBase +((ULONG_PTR)OriginalThunk->u1.AddressOfData & 0xffffffff));
ImportName = (LPSTR)AddressOfData->Name;
NameTable = (PULONG)((ULONG_PTR)ExportBase + (ULONG_PTR)ExportEntry->AddressOfNames);
OrdinalTable = (PUSHORT)((ULONG_PTR)ExportBase + (ULONG_PTR)ExportEntry->AddressOfNameOrdinals);
Hint = AddressOfData->Hint;
Ordinal = LdrpNameToOrdinal(ImportName,ExportEntry->NumberOfNames,ExportBase,NameTable,OrdinalTable);//根据名称找到序号
}
if ((ULONG)Ordinal >= ExportEntry->NumberOfFunctions)
{
}
else
{
AddressOfFunctions = (PULONG)((ULONG_PTR)ExportBase + (ULONG_PTR)ExportEntry->AddressOfFunctions);
Thunk->u1.Function = (ULONG_PTR)ExportBase + AddressOfFunctions;
if ((Thunk->u1.Function > (ULONG_PTR)ExportEntry) && (Thunk->u1.Function < ((ULONG_PTR)ExportEntry + ExportSize)))
{//对于前向索引的动态链Function指向字符串"dll.func"
ImportName = (LPSTR)Thunk->u1.Function;
ForwarderName.Buffer = ImportName;
ForwarderName.Length = (USHORT)(strchr(ImportName, '.') - ImportName);//取得dll名称
ForwarderName.MaximumLength = ForwarderName.Length;
Status = RtlAnsiStringToUnicodeString(&TempUString,&ForwarderName,TRUE);
if (NT_SUCCESS(Status))
{
Status = LdrpLoadDll(FALSE,NULL,NULL,&TempUString,&ForwarderHandle,FALSE);
RtlFreeUnicodeString(&TempUString);
}
RtlInitAnsiString(&ForwarderName,ImportName + ForwarderName.Length + sizeof(CHAR));//取得函数名
if ((ForwarderName.Length > 1) && (*ForwarderName.Buffer == '#'))
{//按序号
ForwardName = NULL;
Status = RtlCharToInteger(ForwarderName.Buffer + sizeof(CHAR),0,&ForwardOrdinal);
}
else
{//按名称
ForwardName = &ForwarderName;
}
Status = LdrpGetProcedureAddress(ForwarderHandle,ForwardName,ForwardOrdinal,(PVOID*)&Thunk->u1.Function,FALSE);//重新获取地址
}
}
USHORT
NTAPI
LdrpNameToOrdinal(IN LPSTR ImportName,
IN ULONG NumberOfNames,
IN PVOID ExportBase,
IN PULONG NameTable,
IN PUSHORT OrdinalTable)
{
LONG Start, End, Next, CmpResult;
/* Use classical binary search to find the ordinal */
Start = Next = 0;
End = NumberOfNames - 1;
while (End >= Start)
{
/* Next will be exactly between Start and End */
Next = (Start + End) >> 1;
/* Compare this name with the one we need to find */
CmpResult = strcmp(ImportName, (PCHAR)((ULONG_PTR)ExportBase + NameTable));
/* We found our entry if result is 0 */
if (!CmpResult) break;
/* We didn't find, update our range then */
if (CmpResult < 0)
{
End = Next - 1;
}
else if (CmpResult > 0)
{
Start = Next + 1;
}
}
/* If end is before start, then the search failed */
if (End < Start) return -1;
/* Return found name */
return OrdinalTable;
}
依据上述过程,我们可以写出一个简化版的GetProcAddress,且不用依赖peb的ldr,输入是pe加载地址和函数名,输出是函数位置,只做查输出表的操作,该函数可以实现我之后要研究的dll隐藏技术。
这份代码除了比原有功能少了通过index获取函数地址之外,增加了一些特殊功能,比如支持unicode,支持顺序查找和二分查找,已在win8.1 x86 x64 环境下测试,若有坑请指出。
#include <windows.h>
#include <winternl.h>
#if defined(WIN64) || defined(_WIN64)
#pragma comment(lib,"ntdll64.lib")
#else
#pragma comment(lib,"ntdll.lib")
#endif
#if defined(UNICODE) || defined(_UNICODE)
#define GetProcAddressT GetProcAddressW
#else
#define GetProcAddresT GetProcAddressA
#endif
/*
改进之处:
1.只允许使用名称索引
2.采用二分查找(后期改成hash)
3.精简代码
4.支持unicode版本
5.加入对资源方式加载dll的处理
*/
typedef struct _MTEB
{
_NT_TIB NtTib;
PVOID ProcessEnvironmentBlock;
}MTEB,*PMTEB;
typedef struct _MPEB
{
DWORD flags;
PVOID Mutant;
PVOID ImageBaseAddress;
}MPEB,*PMPEB;
#define SEARCH_LINEAR 1
#define SEARCH_BINARY 2
FARPROC WINAPI GetProcAddressTA(HMODULE hModule,LPCSTR lpProcName,INT how);
FARPROC WINAPI GetProcAddressTW(HMODULE hModule,LPCWSTR lpProcName,INT how = SEARCH_BINARY)
{
ANSI_STRING ProcNameA;
UNICODE_STRING ProcNameW;
FARPROC ObjAddr = NULL;
if(lpProcName == NULL)
return NULL;
RtlInitUnicodeString(&ProcNameW,lpProcName);
if(!RtlUnicodeStringToAnsiString(&ProcNameA,&ProcNameW,TRUE))
return NULL;
ObjAddr = GetProcAddressTA(hModule,ProcNameA.Buffer,how);
RtlFreeAnsiString(&ProcNameA);
return ObjAddr;
}
#define DLL_RESOURCE_FLAG 3
FARPROC WINAPI GetProcAddressTA(HMODULE hModule,LPCSTR lpProcName,INT how = SEARCH_BINARY)
{
if(!hModule)//获取当前模块
hModule = (HMODULE)((PMPEB)((PMTEB)NtCurrentTeb())->ProcessEnvironmentBlock)->ImageBaseAddress;
BYTE* pFlag = (BYTE*)&hModule;
if(*pFlag & DLL_RESOURCE_FLAG)//还原到'MZ'位置
*pFlag &= ~DLL_RESOURCE_FLAG;
if(hModule == NULL || hModule == (HMODULE)-1)
return NULL;
//找到导出表
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS NtHeaders = NULL;
if(DosHeader->e_magic == IMAGE_DOS_SIGNATURE && DosHeader->e_lfanew)
{//简单检查hModule对应内存PE的合法性
NtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + DosHeader->e_lfanew);
if(NtHeaders->Signature != IMAGE_NT_SIGNATURE)
NtHeaders = NULL;
}
if(!NtHeaders)
return NULL;
PVOID BaseAddress = hModule;
USHORT Directory = IMAGE_DIRECTORY_ENTRY_EXPORT;
PIMAGE_EXPORT_DIRECTORY ExportDir = NULL;
ULONG ExportDirSize = 0;
if(NtHeaders->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
PIMAGE_NT_HEADERS32 tpnh = (PIMAGE_NT_HEADERS32)NtHeaders;
if(Directory >= tpnh->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
DWORD VA = tpnh->OptionalHeader.DataDirectory.VirtualAddress;
if(VA == NULL)
return NULL;
ExportDirSize = tpnh->OptionalHeader.DataDirectory.Size;
ExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)BaseAddress + VA);
}
else
{
PIMAGE_NT_HEADERS64 tpnh = (PIMAGE_NT_HEADERS64)NtHeaders;
if(Directory >= tpnh->OptionalHeader.NumberOfRvaAndSizes)
return NULL;
DWORD VA = tpnh->OptionalHeader.DataDirectory.VirtualAddress;
if(VA == NULL)
return NULL;
ExportDirSize = tpnh->OptionalHeader.DataDirectory.Size;
ExportDir = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)BaseAddress + VA);
}
//匹配导出名,假设按字母排序
PULONG NameTable = (PULONG)((ULONG_PTR)BaseAddress + (ULONG_PTR)ExportDir->AddressOfNames);
PUSHORT OrdinalTable = (PUSHORT)((ULONG_PTR)BaseAddress + (ULONG_PTR)ExportDir->AddressOfNameOrdinals);
USHORT Ordinal = -1;
if(how == SEARCH_LINEAR)
{
for(LONG i = 0;i < ExportDir->NumberOfNames;i++)
{
if(!strcmp(lpProcName,(PCHAR)((ULONG_PTR)BaseAddress + NameTable)))
{
Ordinal = OrdinalTable;
break;
}
}
}
else if(how == SEARCH_BINARY)
{
LONG Start = 0,Next = 0,End = ExportDir->NumberOfNames -1,CmpResult;
while(End >= Start)
{
Next = (Start + End) >> 1;
CmpResult = strcmp(lpProcName,(PCHAR)((ULONG_PTR)BaseAddress + NameTable));
if(!CmpResult)
break;
if(CmpResult < 0)
End = Next - 1;
else
Start = Next + 1;
}
if(End >= Start)
Ordinal = OrdinalTable;
}
//处理forward export情况
if(Ordinal >= ExportDir->NumberOfFunctions)
return NULL;
PULONG AddressOfFunctions = (PULONG)((ULONG_PTR)BaseAddress + (ULONG_PTR)ExportDir->AddressOfFunctions);
ULONG_PTR Function = (ULONG_PTR)BaseAddress + AddressOfFunctions;
if(Function > (ULONG_PTR)ExportDir && Function < (ULONG_PTR)ExportDir + ExportDirSize)
return NULL;
return (FARPROC)Function;
}
void main()
{
typedef int (WINAPI* MSGBOX)(DWORD,CHAR*,CHAR*,DWORD);
HMODULE hmod = LoadLibraryA("user32.dll");
if(hmod == NULL)
return;
MSGBOX box ;
// box = (MSGBOX)GetProcAddress(hmod,"MessageBoxA");
// if(box == NULL)
// return;
// box(NULL,"ok","ok",0);
box = (MSGBOX)GetProcAddressTA(hmod,"MessageBoxA");
if(box == NULL)
return;
box(NULL,"ok","ok",0);
FreeLibrary(hmod);
}
页:
[1]