通过PEB寻找函数地址

  通过PEB的Ldr参数(结构体定义为_PEB_LDR_DATA),遍历当前进程加载的模块信息链表,找到目标模块。
  摘自PEB LDR 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结构体中的InLoadOrderModuleListInMemoryOrderModuleListInInitializationOrderModuleList指向一个当前进程加载模块的链表,链表的每个结点都被定义为_LIST_ENTRY类型的结构体,三条链表以不同方式串连,加载顺序、内存分布顺序、初始化顺序。
  _LIST_ENTRY:

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

其中Flink指向下一结点,尾部结点的Flink则指向头部;Blink指向前一结点,首部节点指向尾部结点;所以该链表结构就是一个双向循环链表。
  除头结点外,_LIST_ENTRY结构体中的两个指针都指向一个_LDR_DATA_TABLE_ENTRY结构体,看这情况也就是说_LDR_DATA_TABLE_ENTRY头部为_LIST_ENTRY咯?该结构体含有当前结点对应的模块的许多信息,根据成员BaseDllName匹配需要的已加载模块,再由DllBase得到句柄。
  在通过InLoadOrderLinks进行模块查找时,Flink或者Blink可直接作为_LDR_DATA_TABLE_ENTRY地址;如果通过InMemoryOrderLinksInInitializationOrderLinks 进行匹配时,需要将F(B)link地址偏移-0x08-0x10作为地址,与两者在_LDR_DATA_TABLE_ENTRY结构体中的偏移相对应。

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

  测试不调用系统API,利用PEB寻找模块,并通过模块寻找目标函数;这种情况大多是在Shellcode中用到,比方说恶意程序、病毒等;在许多情况下shellcode通常作为独立代码执行,不被加载器基址重定位,也无法直接调用API,所以通过PEB查找目标模块,进而查找目标函数,通常首先都会获取LoadLibraryAGetProcAddress地址,便于之后直接加载指定模块,获取导出函数并调用。
  写的时候我发现从函数序数表得到的函数序号减去序号基数base会得到不正确结果,不减则正确,代码调试时得到base值为1
  导出表结构:

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;

测试代码,写的时候在通过模块获取函数地址的时候没用汇编,要提二进制码还得重写这个部分;不过顺便温习一下导出表结构。

#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 targetModulecall Lowermov eax, fs:[30h]       // eax <- pebmov eax, [eax + 0ch]        // eax <- Ldr  _PEB_LDR_DATAmov eax, [eax + 0ch]        // eax <- first Flink address, InLoadOrderModuleList [Type: _LIST_ENTRY]_LOOP :push eaxmov eax, [eax + 2ch + 4]        // dll name string addresscmp eax, 0jz _END             // 字符串为NULL,说明寻找完毕,退出lea ebx, dllNamepush ebx                // for calling comparepush ebx                // for calling lower string_COPYNAME :mov dl, byte ptr[eax]mov byte ptr[ebx], dl   // copy nameadd ebx, 2add eax, 2cmp[eax], 0jnz _COPYNAMEmov[ebx], 0call Lower              // lower dll name stringpush targetModulecall __strcmpW          // compare dll namecmp al, 1jz _FOUNDpop eaxmov eax, [eax]          // next Flinkjmp _LOOP               // if not found, go to next flink and loop again_FOUND :pop eaxpush DWORD ptr[eax + 18h]   // save dllBasepop dllBasemov 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);}elseprintf("\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值 MZprintf("\tMatch \"MZ\" magic :)\n");elseprintf("\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)        // 签名 PEprintf("\tMatch \"PE\" signature :)\n");elseprintf("\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);  // WORDprintf("\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, procLoadlibpush eax    //LoadLibraryApush hModcall GetProcByhModcmp eax, 0jz _END2mov ebx,eaxlea eax, tarMod // target mod; user32.dllpush eaxcall ebx        // call LoadLibraryAcmp eax,0jz _END2push eax    // save hInstance valuelea eax,procGetProc     // string GetProcAddresspush eaxpush hModcall GetProcByhModcmp eax, 0jz _END2mov ebx, eaxlea eax, targFuncpop edxpush eax    // messageboxapush edx    // target hModcall ebx        // call getprocaddresscmp eax, 0jz _END2mov ebx, eaxpush MB_OKlea eax, testpush eaxpush eaxpush 0          // param for messageboxcall ebx    // call got api - messageboxA_END2:}}
}

转载于:https://www.cnblogs.com/zUotTe0/p/11421173.html


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部