欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]某驱动里的一处EAT Hook函数分析

[原创]某驱动里的一处EAT Hook函数分析

对某驱动里的一处EAT Hook函数进行了分析,由于技术比较菜,费了大半天时间,不过也学到了不少,在此分享总结下。
EAT Hook的流程没什么好说的,找到EAT里需要Hook的函数地址,关掉页保护,原子写入,再恢复页保护。稍微有点特色的地方在于函数名匹配时的二分查找,这地方也是耗费我时间最多的地方。
不多说,直接上码:


.text:000104AC ; ULONG __stdcall EATHook(PVOID ImageBase, PANSI_STRING pstrExportName, ULONG HookFunc, ULONG* pFuncRVA)

.text:000104AC pNameOrdinals   = dword ptr -14h

.text:000104AC pszExportName   = dword ptr -10h

.text:000104AC pNames          = dword ptr -0Ch

.text:000104AC Size            = dword ptr -8

.text:000104AC i               = dword ptr -4

.text:000104AC ImageBase       = dword ptr  8

.text:000104AC pstrExportName  = dword ptr  0Ch

.text:000104AC HookFunc        = dword ptr  10h

.text:000104AC pFuncRVA        = dword ptr  14h

.text:000104AC

.text:000104AC                 mov     edi, edi

.text:000104AE                 push    ebp

.text:000104AF                 mov     ebp, esp

.text:000104B1                 sub     esp, 14h

.text:000104B4                 push    ebx

.text:000104B5                 push    esi

.text:000104B6                 push    edi

.text:000104B7                 lea     eax, [ebp+Size]

.text:000104BA                 push    eax             ; Size

.text:000104BB                 push    0               ; DirectoryEntry, 导出表

.text:000104BD                 push    1               ; MappedAsImage, 返回VA

.text:000104BF                 push    [ebp+ImageBase] ; ImageBase

.text:000104C2                 call    ds:RtlImageDirectoryEntryToData

.text:000104C8                 test    eax, eax

.text:000104CA                 jz      loc_10563       ; return 0

.text:000104D0                 mov     edx, [eax+20h]  ; _IMAGE_EXPORT_DIRECTORY.AddressOfNames

.text:000104D3                 mov     ecx, [eax+24h]  ; _IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals

.text:000104D6                 add     edx, [ebp+ImageBase] ; ImageBase + AddressOfNames

.text:000104D9                 add     ecx, [ebp+ImageBase] ; ImageBase + AddressOfNameOrdinals

.text:000104DC                 mov     edi, [eax+18h]  ; _IMAGE_EXPORT_DIRECTORY.NumberOfNames

.text:000104DF                 and     [ebp+i], 0      ; i = 0

.text:000104E3                 dec     edi             ; NumberOfNames - 1

.text:000104E4                 mov     [ebp+pNames], edx

.text:000104E7                 mov     [ebp+pNameOrdinals], ecx

.text:000104EA                 js      short loc_10563 ; NumberOfNames为0时return 0

.text:000104EC                 mov     ecx, [ebp+pstrExportName]

.text:000104EF                 mov     ecx, [ecx+4]    ; ANSI_STRING.Buffer

.text:000104F2                 mov     [ebp+pszExportName], ecx ; pszExportName = pstrExportName->Buffer

.text:000104F5                 jmp     short loc_104FA

.text:000104F7 ; ---------------------------------------------------------------------------

.text:000104F7

.text:000104F7 loc_104F7:

.text:000104F7                 mov     edx, [ebp+pNames]

.text:000104FA

.text:000104FA loc_104FA:

.text:000104FA                 mov     ecx, [ebp+i]

.text:000104FD                 add     ecx, edi

.text:000104FF                 sar     ecx, 1          ; 二分查找

.text:00010501                 mov     esi, [edx+ecx*4] ; _IMAGE_EXPORT_DIRECTORY.AddressOfNames数组

.text:00010504                 mov     edx, [ebp+pszExportName]

.text:00010507                 add     esi, [ebp+ImageBase] ; ImageBase + Export Name's RVA

.text:0001050A                 mov     [ebp+pstrExportName], edx

.text:0001050D

.text:0001050D loc_1050D:

.text:0001050D                 mov     edx, [ebp+pstrExportName] ; 字符串匹配, 每两个字节匹配

.text:00010510                 mov     bl, [edx]       ; 第一个字节

.text:00010512                 mov     dl, bl

.text:00010514                 cmp     bl, [esi]

.text:00010516                 jnz     short loc_10537 ; 不匹配则跳转, 注意这里的通过cf判断字符大小: cf=1表示导出表里的名字比参数的字符大

.text:00010518                 test    dl, dl

.text:0001051A                 jz      short loc_10533 ; 匹配结束结束则跳

.text:0001051C                 mov     edx, [ebp+pstrExportName]

.text:0001051F                 mov     bl, [edx+1]     ; 第二字节

.text:00010522                 mov     dl, bl

.text:00010524                 cmp     bl, [esi+1]

.text:00010527                 jnz     short loc_10537 ; 不匹配则跳

.text:00010529                 add     [ebp+pstrExportName], 2

.text:0001052D                 inc     esi

.text:0001052E                 inc     esi

.text:0001052F                 test    dl, dl

.text:00010531                 jnz     short loc_1050D ; 匹配未结束则跳

.text:00010533

.text:00010533 loc_10533:

.text:00010533                 xor     edx, edx        ; 匹配上了到这里

.text:00010535                 jmp     short loc_1053C

.text:00010537 ; ---------------------------------------------------------------------------

.text:00010537

.text:00010537 loc_10537:

.text:00010537                 sbb     edx, edx        ; 不匹配到这里

.text:00010539                 sbb     edx, 0FFFFFFFFh ; cf=0时edx=1, cf=1时edx=-1

.text:0001053C

.text:0001053C loc_1053C:

.text:0001053C                 test    edx, edx

.text:0001053E                 jge     short loc_10545 ; edx>=0时跳

.text:00010540                 lea     edi, [ecx-1]    ; 到这里意味这edx<0, 即上面edx=-1的情况, 即导出表中的名字比参数的字符大

.text:00010543                 jmp     short loc_1054D ; 导出表的名字按字母顺序排列, 因此要调整比较的Name, 通过这里的ecx - 1对Name数组下标-1

.text:00010545 ; ---------------------------------------------------------------------------

.text:00010545

.text:00010545 loc_10545:

.text:00010545                 jle     short loc_10552 ; 匹配到

.text:00010547                 lea     edx, [ecx+1]    ; 到这里意味着导出表Name数组下标要调整为+1

.text:0001054A                 mov     [ebp+i], edx    ; 参数字符串肯定是要比ecx + 1的下标Name大了, 故要更新最小下标值

.text:0001054D

.text:0001054D loc_1054D:

.text:0001054D                 cmp     edi, [ebp+i]    ; i其实是二分查找里的最小下标值

.text:00010550                 jge     short loc_104F7 ; edi为二分查找里的最大下标值, 因此edi >= i时继续二分查找

.text:00010552

.text:00010552 loc_10552:

.text:00010552                 cmp     edi, [ebp+i]

.text:00010555                 jl      short loc_10563 ; 二分查找没找到return 0

.text:00010557                 mov     edx, [ebp+pNameOrdinals]

.text:0001055A                 movzx   ecx, word ptr [edx+ecx*2]

.text:0001055E                 cmp     ecx, [eax+14h]  ; ecx为匹配到的函数Ordinal, 与_IMAGE_EXPORT_DIRECTORY.NumberOfFunctions作比较

.text:00010561                 jb      short loc_10567 ; 不小于则return 0

.text:00010563

.text:00010563 loc_10563:

.text:00010563                 xor     eax, eax

.text:00010565                 jmp     short loc_1059E

.text:00010567 ; ---------------------------------------------------------------------------

.text:00010567

.text:00010567 loc_10567:

.text:00010567                 mov     eax, [eax+1Ch]  ; _IMAGE_EXPORT_DIRECTORY.AddressOfFunctions

.text:0001056A                 add     eax, [ebp+ImageBase]

.text:0001056D                 mov     edx, [ebp+HookFunc]

.text:00010570                 lea     edi, [eax+ecx*4]

.text:00010573                 mov     esi, [edi]      ; 匹配到的函数的RVA

.text:00010575                 add     esi, [ebp+ImageBase]

.text:00010578                 test    edx, edx

.text:0001057A                 jz      short loc_10597

.text:0001057C                 call    PageProtectOff

.text:00010581                 sub     edx, [ebp+ImageBase] ; Value, 计算Hook函数相对ImageBase的RVA

.text:00010584                 mov     ecx, edi        ; Target, EAT Hook

.text:00010586                 call    ds:InterlockedExchange

.text:0001058C                 sti                     ; PageProtectOn

.text:0001058D                 push    eax

.text:0001058E                 mov     eax, OldCR0

.text:00010593                 mov     cr0, eax

.text:00010596                 pop     eax

.text:00010597

.text:00010597 loc_10597:

.text:00010597                 mov     eax, [ebp+pFuncRVA]

.text:0001059A                 mov     [eax], edi      ; 匹配到的函数的RVA

.text:0001059C                 mov     eax, esi        ; return匹配到的函数地址

.text:0001059E

.text:0001059E loc_1059E:

.text:0001059E                 pop     edi

.text:0001059F                 pop     esi

.text:000105A0                 pop     ebx

.text:000105A1                 leave

.text:000105A2                 retn    10h

.text:000105A2 EATHook         endp


由于分析得少,技能不行,sbb这里折腾了好久,估计不少新手也会弄不明白,这里单独提一下。sbb是两个操作数相减再减去cf位,将结果存放在操作数一里面。

.text:00010514                 cmp     bl, [esi]

.text:00010516                 jnz     short loc_10537 ; 不匹配则跳转, 注意这里的通过cf判断字符大小: cf=1表示导出表里的名字比参数的字符大

......

.text:00010537 loc_10537:

.text:00010537                 sbb     edx, edx        ; 不匹配到这里

.text:00010539                 sbb     edx, 0FFFFFFFFh ; cf=0时edx=1, cf=1时edx=-1


.text:00010514 cmp这里会影响到cf,因此到sbb edx, edx时,edx会有0和-1两种情况,再sbb一下-1就会有1和-1两种情况。
考虑到有人可能看到汇编会头疼,这里写了类似的C代码:

ULONG EATHook(ULONG ulImageBase, ANSI_STRING* lpastrExportName, ULONG ulHookFunc, ULONG* pulOrgFuncRVA)

{

	ULONG ulRet = 0;

	do 

	{

		if(NULL == lpastrExportName || NULL == pulOrgFuncRVA)

		{

			break;

		}

		KdPrint(("EATHook %s.\n", lpastrExportName->Buffer));

		ULONG ulSize = 0;

		PIMAGE_EXPORT_DIRECTORY pDirectory = RtlImageDirectoryEntryToData(ulImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize);

		if(NULL == pDirectory)

		{

			break;

		}

		int nHighIndex = pDirectory->NumberOfNames - 1;

		if(nHighIndex < 0)

		{

			break;	// NumberOfNames为0

		}

		int nLowIndex = 0;

		int nMidIndex = 0;

		char* pszExportName = lpastrExportName->Buffer;

		BOOL bMatch = FALSE;

		while(nHighIndex >= nLowIndex)

		{

			nMidIndex = (nLowIndex + nHighIndex) / 2;

			char* pBuffer = (char*)(((ULONG*)(pDirectory->AddressOfNames + ulImageBase))[nMidIndex] + ulImageBase);

			KdPrint(("nLowIndex: %d, nMidIndex: %d, nHightIndex: %d, ExportName: %s.\n", nLowIndex, nMidIndex, nHighIndex, pBuffer));

			int i = 0;

			for(; pszExportName[i] != 0; ++i)

			{

				if(pszExportName[i] - pBuffer[i] > 0)

				{

					nLowIndex = nMidIndex;

					break;

				}

				else if(pszExportName[i] - pBuffer[i] < 0)

				{

					nHighIndex = nMidIndex;

					break;

				}

				else

				{

					// 单个字符匹配

				}

			}

			if(0 == pszExportName[i])

			{

				// 完全匹配

				bMatch = TRUE;

				break;

			}

		}

		if(!bMatch)

		{

			break;

		}

		WORD wOrdinal = ((WORD*)(pDirectory->AddressOfNameOrdinals + ulImageBase))[nMidIndex];

		KdPrint(("Ordinal: %d, NumberOfFunctions: %d.\n", wOrdinal, pDirectory->NumberOfFunctions));

		if(wOrdinal >= pDirectory->NumberOfFunctions)

		{

			break;

		}

		ULONG ulAddressOfFunctions = pDirectory->AddressOfFunctions + ulImageBase;

		*pulOrgFuncRVA = ((ULONG*)ulAddressOfFunctions)[wOrdinal];

		ulRet = *pulOrgFuncRVA + ulImageBase;

		if(ulHookFunc != 0)

		{

			PageProtectOff();

			InterlockedExchange(&((LONG*)ulAddressOfFunctions)[wOrdinal], ulHookFunc - ulImageBase);

			PageProtectOn();

		}

	} while (0);

	return ulRet;

}


调用该函数找了下KeServiceDescriptorTable和ZwCreateKey:

上传的附件 EAT Hook.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功