通過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("\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:
		}
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM