元始天尊 发表于 2015-6-13 12:42:57

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;
}

元始天尊 发表于 2015-6-13 16:14:56

依据上述过程,我们可以写出一个简化版的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]
查看完整版本: GetProcAddress解析(一)