• 通过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 image
        DWORD   AddressOfNames;         // RVA from base of image
        DWORD   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("	==lower string : %ws
    ", s);
    }
    
     BOOL WINAPI __strcmpW(WCHAR* a, WCHAR *b) {
    	//printf("	compared dll name: %ws
    
    ", 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
    ");
    	/*
    		通过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("	[ok] Have found target module :)
    ");
    		printf("		DllBase : %#x
    		Dll Name: %ws
    
    ", dllBase, targetModule);
    	}
    	else
    		printf("	[no] Not found :(
    
    ");
    
    	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
    ");
    
    	if ((WORD)pIDH->e_magic == 0x5a4d)		// magic值 MZ
    		printf("	Match "MZ" magic :)
    ");
    	else
    		printf("	Not Match "MZ" magic :(
    ");
    
    	pINH = (PIMAGE_NT_HEADERS)(pIDH->e_lfanew+(DWORD)hMod);
    	/*
    	printf("offset : %#x
    ", pIDH->e_lfanew);
    	printf("Image Base : %#x
    ", hMod);
    	printf("PIMAGE_NT_HEADERS value : %#x
    ", pINH);
    	*/
    	if ((WORD)pINH->Signature == 0x4550)		// 签名 PE
    		printf("	Match "PE" signature :)
    ");
    	else
    		printf("	Not Match "PE" signature :(
    ");
    
    	pIDD = (PIMAGE_DATA_DIRECTORY)((pINH->OptionalHeader).DataDirectory);	// 数据目录表
    	pIED = (PIMAGE_EXPORT_DIRECTORY)(pIDD[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + (DWORD)hMod);
    	printf("	export table VA : %#x
    	function names array address : %#x
    ", (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
    ", name);
    		
    			通过functionames数组获取下标,根据该下标(输出函数名表和输出序号表一一对应)在输出序号表
    			获取函数地址表中的序号,将序号减去基数作为下标寻找到函数地址RVA。
    		*/
    		MultiByteToWideChar(CP_ACP, NULL, name, ++length, funcName, length);
    		//printf("	compared function name : %ws
    ", funcName);
    		Lower(funcName);
    		if (__strcmpW(procName, funcName)) {
    			printf("	[ok] succeedfound function name :)
    ");
    			ordinal = *((WORD*)(pIED->AddressOfNameOrdinals + (DWORD)hMod) + i);  // WORD
    			printf("		index of target function : %#x
    		ordinal number : %#x
    		orinal base : %#x
    ", i, ordinal, pIED->Base);
    			funcAddr = *((DWORD*)(pIED->AddressOfFunctions + (DWORD)hMod) + (ordinal/* - pIED->Base加上之后不对*/)) + (DWORD)hMod;
    			printf("	Get function address : %#x
    ", funcAddr);
    			break;
    		}
    	}
    	if (!funcAddr)
    		printf("	[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:
    		}
    	}
    }
    
    
  • 相关阅读:
    与众不同 LibreOJ
    清点人数 LibreOJ
    数星星 Stars LibreOJ
    树状数组 1 :单点修改,区间查询 Gym
    DDL与DML
    605. 种花问题
    Minimum Spanning Tree Gym
    最小生成树动态演示
    ssm框架添加分页
    ssm框架下的文件自动生成模板(简单方便)
  • 原文地址:https://www.cnblogs.com/zUotTe0/p/11421173.html
Copyright © 2020-2023  润新知