Looking function address by PEB

  Ldr parameters by the PEB (structure defined as _PEB_LDR_DATA), traverse the linked list of the current process module information loaded, to find the target module.
  Taken PEB LDR the DATA :

typedef struct _PEB_LDR_DATA
{
    0x00    ULONG         Length;                            /* Size of structure, used by ntdll.dll as structure version ID */
    0x04    BOOLEAN       Initialized;                       /* If set, loader data section for current process is initialized */
    0x08    PVOID         SsHandle;
    0x0c    LIST_ENTRY    InLoadOrderModuleList;             /* Pointer to LDR_DATA_TABLE_ENTRY structure. Previous and next module in load order */
    0x14    LIST_ENTRY    InMemoryOrderModuleList;           /* Pointer to LDR_DATA_TABLE_ENTRY structure. Previous and next module in memory placement order */
    0x1c    LIST_ENTRY    InInitializationOrderModuleList;   /* Pointer to LDR_DATA_TABLE_ENTRY structure. Previous and next module in initialization order */
} PEB_LDR_DATA, *PPEB_LDR_DATA; // +0x24

  _PEB_LDR_DATA structural body InLoadOrderModuleList, InMemoryOrderModuleList, InInitializationOrderModuleListpoints to a linked list the current process is loaded module list of each node is defined as _LIST_ENTRYa structure type, in three different ways series chain, the loading sequence, the order of the distribution of memory, the initialization sequence.
  _LIST_ENTRY:

0:000> dt ntdll!_LIST_ENTRY
   +0x000 Flink            : Ptr32 _LIST_ENTRY
   +0x004 Blink            : Ptr32 _LIST_ENTRY

Which Flinkpoints to the next node, tail node Flinkpoints to the head; Blinkpoint to the previous node, the first node pointing to the tail portion of the node; so the list is a doubly linked, circular list structure.
  In addition to the first node, _LIST_ENTRYthe structure of the two pointers point to a _LDR_DATA_TABLE_ENTRYstructure, that is to see this situation _LDR_DATA_TABLE_ENTRYthe head is _LIST_ENTRYslightly? This structure contains a lot of information corresponding to the current node module, according to the members BaseDllName loaded module needs to match, then the DllBase get a handle.
  By InLoadOrderLinksthe time a module lookup, Flinkor Blinkmay be directly used as _LDR_DATA_TABLE_ENTRYthe address; if by InMemoryOrderLinksor InInitializationOrderLinksduring matching, it is necessary to F(B)linkaddress offset -0x08or -0x10as an address to both the _LDR_DATA_TABLE_ENTRYshift in the structure corresponds.

0:000> dt ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
   +0x018 DllBase          : Ptr32 Void
   +0x01c EntryPoint       : Ptr32 Void
   +0x020 SizeOfImage      : Uint4B
   +0x024 FullDllName      : _UNICODE_STRING
   +0x02c BaseDllName      : _UNICODE_STRING
   +0x034 FlagGroup        : [4] UChar
   +0x034 Flags            : Uint4B
   +0x034 PackagedBinary   : Pos 0, 1 Bit
   +0x034 MarkedForRemoval : Pos 1, 1 Bit
   +0x034 ImageDll         : Pos 2, 1 Bit
   +0x034 LoadNotificationsSent : Pos 3, 1 Bit
   +0x034 TelemetryEntryProcessed : Pos 4, 1 Bit
   +0x034 ProcessStaticImport : Pos 5, 1 Bit
   +0x034 InLegacyLists    : Pos 6, 1 Bit
   +0x034 InIndexes        : Pos 7, 1 Bit
   +0x034 ShimDll          : Pos 8, 1 Bit
   +0x034 InExceptionTable : Pos 9, 1 Bit
   +0x034 ReservedFlags1   : Pos 10, 2 Bits
   +0x034 LoadInProgress   : Pos 12, 1 Bit
   +0x034 LoadConfigProcessed : Pos 13, 1 Bit
   +0x034 EntryProcessed   : Pos 14, 1 Bit
   +0x034 ProtectDelayLoad : Pos 15, 1 Bit
   +0x034 ReservedFlags3   : Pos 16, 2 Bits
   +0x034 DontCallForThreads : Pos 18, 1 Bit
   +0x034 ProcessAttachCalled : Pos 19, 1 Bit
   +0x034 ProcessAttachFailed : Pos 20, 1 Bit
   +0x034 CorDeferredValidate : Pos 21, 1 Bit
   +0x034 CorImage         : Pos 22, 1 Bit
   +0x034 DontRelocate     : Pos 23, 1 Bit
   +0x034 CorILOnly        : Pos 24, 1 Bit
   +0x034 ChpeImage        : Pos 25, 1 Bit
   +0x034 ReservedFlags5   : Pos 26, 2 Bits
   +0x034 Redirected       : Pos 28, 1 Bit
   +0x034 ReservedFlags6   : Pos 29, 2 Bits
   +0x034 CompatDatabaseProcessed : Pos 31, 1 Bit
   +0x038 ObsoleteLoadCount : Uint2B
   +0x03a TlsIndex         : Uint2B
   +0x03c HashLinks        : _LIST_ENTRY
   +0x044 TimeDateStamp    : Uint4B
   +0x048 EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT
   +0x04c Lock             : Ptr32 Void
   +0x050 DdagNode         : Ptr32 _LDR_DDAG_NODE
   +0x054 NodeModuleLink   : _LIST_ENTRY
   +0x05c LoadContext      : Ptr32 _LDRP_LOAD_CONTEXT
   +0x060 ParentDllBase    : Ptr32 Void
   +0x064 SwitchBackContext : Ptr32 Void
   +0x068 BaseAddressIndexNode : _RTL_BALANCED_NODE
   +0x074 MappingInfoIndexNode : _RTL_BALANCED_NODE
   +0x080 OriginalBase     : Uint4B
   +0x088 LoadTime         : _LARGE_INTEGER
   +0x090 BaseNameHashValue : Uint4B
   +0x094 LoadReason       : _LDR_DLL_LOAD_REASON
   +0x098 ImplicitPathOptions : Uint4B
   +0x09c ReferenceCount   : Uint4B
   +0x0a0 DependentLoadFlags : Uint4B
   +0x0a4 SigningLevel     : UChar

  Test does not call the system API, the use of PEB find module by module and look for the objective function; this situation is mostly used in Shellcode in, say malicious programs and viruses; in many cases, usually as a stand-alone code shellcode execution, not loader base relocation, can not call the API directly, so find the target module PEB, and then look for the objective function, usually first will get LoadLibraryAand GetProcAddressaddress, for direct load the specified module after obtaining export function and call.
  I found the time to write from 函数序数表minus function serial number to get the base basewill get an incorrect result, diminished the right to obtain the code when debugging basevalue 1.
  Export table structure:

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;

Test code, compile time to write useless when obtaining function address through module to mention the binary code will have to rewrite this section; but the way to brush up on Export table structure.

#include "windows.h"
#include "stdio.h"

//typedef void(*func)();
VOID WINAPI Lower(WCHAR* s) {
    WCHAR* pos = s;
    for (; *pos; pos++) {
        if (*pos <= 'Z' && *pos >= 'A')
            *pos |= 0x20;
    }
    //printf("\t==lower string : %ws\n", s);
}

 BOOL WINAPI __strcmpW(WCHAR* a, WCHAR *b) {
    //printf("\tcompared dll name: %ws\n\n", b);

    int i = 0;
    for (i = 0; a[i] || b[i]; i++)
        if (a[i] != b[i])
            return FALSE;
    return TRUE;
}

HMODULE WINAPI FindModuleByPeb(WCHAR* targetModule) {
    WCHAR dllName[50] = { 0 };
    BOOL foundModule = FALSE;
    DWORD dllBase = NULL; 
    printf("[#] start get module handle\n");
    /*
        通过PEB结构中的Ldr寻找到InLoadOrderModuleList,遍历寻找已加载的模块,通过模块名进行寻找
    */
    __asm {
        push targetModule
        call Lower
        mov eax, fs:[30h]       // eax <- peb
        mov eax, [eax + 0ch]        // eax <- Ldr  _PEB_LDR_DATA
        mov eax, [eax + 0ch]        // eax <- first Flink address, InLoadOrderModuleList [Type: _LIST_ENTRY]
    _LOOP :
        push eax
        mov eax, [eax + 2ch + 4]        // dll name string address
        cmp eax, 0
        jz _END             // 字符串为NULL,说明寻找完毕,退出
        lea ebx, dllName
        push ebx                // for calling compare
        push ebx                // for calling lower string
    _COPYNAME :
        mov dl, byte ptr[eax]
        mov byte ptr[ebx], dl   // copy name
        add ebx, 2
        add eax, 2
        cmp[eax], 0
        jnz _COPYNAME
        mov[ebx], 0
        call Lower              // lower dll name string
        push targetModule
        call __strcmpW          // compare dll name
        cmp al, 1
        jz _FOUND
        pop eax
        mov eax, [eax]          // next Flink
        jmp _LOOP               // if not found, go to next flink and loop again
    _FOUND :
        pop eax
        push DWORD ptr[eax + 18h]   // save dllBase
        pop dllBase
        mov foundModule, 1      // found target dll
    _END :
    }
    if (foundModule) {
        printf("\t[ok] Have found target module :)\n");
        printf("\t\tDllBase : %#x\n\t\tDll Name: %ws\n\n", dllBase, targetModule);
    }
    else
        printf("\t[no] Not found :(\n\n");

    return (HMODULE)dllBase;
}

func WINAPI GetProcByhMod(HMODULE hMod, WCHAR* procName) {

    PIMAGE_DOS_HEADER pIDH = NULL;      //DOS 头
    PIMAGE_NT_HEADERS pINH = NULL;      // NT头
    PIMAGE_DATA_DIRECTORY pIDD = NULL;  // 数据目录表
    PIMAGE_EXPORT_DIRECTORY pIED = NULL; // 导出表
    INT i = 0, length = 0;
    WORD ordinal = -1;
    DWORD funcAddr = NULL;

    WCHAR funcName[60] = { 0 };     // 函数名字
    CHAR *name = NULL;

    pIDH = (PIMAGE_DOS_HEADER)hMod;
    printf("[#]start Get Library By found module handle\n");

    if ((WORD)pIDH->e_magic == 0x5a4d)      // magic值 MZ
        printf("\tMatch \"MZ\" magic :)\n");
    else
        printf("\tNot Match \"MZ\" magic :(\n");

    pINH = (PIMAGE_NT_HEADERS)(pIDH->e_lfanew+(DWORD)hMod);
    /*
    printf("offset : %#x\n", pIDH->e_lfanew);
    printf("Image Base : %#x\n", hMod);
    printf("PIMAGE_NT_HEADERS value : %#x\n", pINH);
    */
    if ((WORD)pINH->Signature == 0x4550)        // 签名 PE
        printf("\tMatch \"PE\" signature :)\n");
    else
        printf("\tNot Match \"PE\" signature :(\n");

    pIDD = (PIMAGE_DATA_DIRECTORY)((pINH->OptionalHeader).DataDirectory);   // 数据目录表
    pIED = (PIMAGE_EXPORT_DIRECTORY)(pIDD[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (DWORD)hMod);
    printf("\texport table VA : %#x\n\tfunction names array address : %#x\n", (DWORD)pIED, pIED->AddressOfNames + (DWORD)hMod);

    Lower(procName);    //

    for (i = 0; i < pIED->NumberOfNames; i++) {
        name = (CHAR*)(*((DWORD*)(pIED->AddressOfNames + (DWORD)hMod) + i) + (DWORD)hMod);
        for (length = 0; name[length]; length++);   // 函数名长度
        /*printf("==> %s\n", name);
        
            通过functionames数组获取下标,根据该下标(输出函数名表和输出序号表一一对应)在输出序号表
            获取函数地址表中的序号,将序号减去基数作为下标寻找到函数地址RVA。
        */
        MultiByteToWideChar(CP_ACP, NULL, name, ++length, funcName, length);
        //printf("\tcompared function name : %ws\n", funcName);
        Lower(funcName);
        if (__strcmpW(procName, funcName)) {
            printf("\t[ok] succeedfound function name :)\n");
            ordinal = *((WORD*)(pIED->AddressOfNameOrdinals + (DWORD)hMod) + i);  // WORD
            printf("\t\tindex of target function : %#x\n\t\tordinal number : %#x\n\t\torinal base : %#x\n", i, ordinal, pIED->Base);
            funcAddr = *((DWORD*)(pIED->AddressOfFunctions + (DWORD)hMod) + (ordinal/* - pIED->Base加上之后不对*/)) + (DWORD)hMod;
            printf("\tGet function address : %#x\n", funcAddr);
            break;
        }
    }
    if (!funcAddr)
        printf("\t[no] not Found target function :(");
    return (func)funcAddr;
}

INT main(INT argc, CHAR* argv[]) {
    WCHAR searchMod[] = { L"Kernel32.dll" };
    WCHAR procLoadlib[] = { L"LoadLibraryA" };
    WCHAR procGetProc[] = { L"GetProcAddress" };

    //func procAddr = NULL;

    //
    CHAR tarMod[] = { "User32.dll" };
    CHAR targFunc[] = { "MessageBoxA" };    // 测试弹窗
    CHAR test[] = { "test" };/////

    /*HMODULE hMod = LoadLibraryA(tarMod);
    typedef int (*msgBoxProc)(HWND, LPCTSTR, LPCTSTR, UINT);
    msgBoxProc f = (msgBoxProc)GetProcAddress(hMod, targFunc);
    f(NULL, (LPCTSTR)"test", (LPCTSTR)"test", MB_OK);*/

    HMODULE hMod = FindModuleByPeb(searchMod);
    if (hMod) {
        __asm {
            lea eax, procLoadlib
            push eax    //LoadLibraryA
            push hMod
            call GetProcByhMod
            cmp eax, 0
            jz _END2
            mov ebx,eax
            lea eax, tarMod // target mod; user32.dll
            push eax
            call ebx        // call LoadLibraryA
            cmp eax,0
            jz _END2
            push eax    // save hInstance value
            lea eax,procGetProc     // string GetProcAddress
            push eax
            push hMod
            call GetProcByhMod
            cmp eax, 0
            jz _END2
            mov ebx, eax
            lea eax, targFunc
            pop edx
            push eax    // messageboxa
            push edx    // target hMod
            call ebx        // call getprocaddress
            cmp eax, 0
            jz _END2
            mov ebx, eax
            push MB_OK
            lea eax, test
            push eax
            push eax
            push 0          // param for messagebox
            call ebx    // call got api - messageboxA
        _END2:
        }
    }
}

Guess you like

Origin www.cnblogs.com/zUotTe0/p/11421173.html