对某驱动里的一处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
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课