Hook示例及Syscall绕过

admin 2022年7月8日18:33:55评论225 views字数 6374阅读21分14秒阅读模式
0x01 前言

  本文主要讲解应用层hook种类、技术以及利用Syscall进行绕过。

0x02 应用层hook种类
  在实战应用层中hook技术多种多样,主要有消息hook、注入hook、调试hook三大类,具体的见下图:

Hook示例及Syscall绕过


0x03 hook运行流程及示例

正常调用流程:

Hook示例及Syscall绕过


HOOK后调用流程:

Hook示例及Syscall绕过


创建一个代理对象,然后把原始对象替换为我们的代理对象,这样就可以在这个代理对象为所欲为,例如修改参数或替换返回值。

以Inlinehook来举例说明,步骤如下:
1.原函数挂钩修改成跳转代理函数指令;
2.代理函数执行指令完成后执行被修改指令或者修改回去后再跳转回挂钩点;
3.程序恢复正常运行。

Hook示例及Syscall绕过



Inlinehook示例与绕过:

示例代码为编译时将shellcode存放于.edata段内,使用时对pe文件解析获取.edata段的内存起始地址,因为该段内存无可执行属性,使用virtualprotect修改内存属性为可执行,然后跳转到shellcode执行 shellcode为一段弹框代码。

程序代码:

Hook示例及Syscall绕过


Hook示例及Syscall绕过


通过对进程注入dll 并在dll入口处对VirtualProtect进行Inline hook。

修改函数流程:

Hook示例及Syscall绕过

伪造函数:

Hook示例及Syscall绕过


未完成hook时,点击按钮正常运行shellcode代码内容:

Hook示例及Syscall绕过


完成HOOK函数virtualprotect后,再次点击捕捉到virtualprotect函数调用:

Hook示例及Syscall绕过


32位程序调用VirtualProtect调用链:
1、调用VirtualProtect 模块 kernel32

Hook示例及Syscall绕过


2.VirtualProtect内部调用ntdll ZwProtectVirtualMemory

Hook示例及Syscall绕过


3.ntdll ZwProtectVirtualMemory 实现

Hook示例及Syscall绕过


VirtualProtect用户层至内核层调用流程

VirtualProtect(Kernel32)--->ZwProtectVirtualMemory(ntdll)--->sysenter

0x04 绕过

4.1 如何绕过三环的系统APIhook 

通过上图可知ntdll 进入0环调用函数实体 是利用syscall进入的,那么模仿一个ntdll的syscall进入过程就可以绕开所有用户层的hook。

hell's Gate(地狱之门)该项目为首个披露并实现整个模拟syscall调用的首个项目,但地狱之门的代码也有一定的局限性。例如存在被hook后的函数不能识别获取兼容性存在问题,不易用等问题。后面由众多师傅改良,最终比较推荐SysWhispers2、SysWhispers3。SysWhispers3为SysWhispers2的升级版本,添加了多个逃避AV/EDR检测手法 如:EGG。感兴趣的师傅可以自行去下载源码查阅资料。
  • hell's Gate 项目地址: https://github.com/am0nsec/HellsGate

  • SysWhispers2 项目地址: https://github.com/jthuraisamy/SysWhispers2

  • SysWhispers3 项目地址:https://github.com/klezVirus/SysWhispers3

4.2 主要思路

通过模拟函数调用流程自行构造并调用Syscall进入内核。
而在此之前还必须解决两个问题:
  1. 使用syscall时必须知道函数调用号;

  2. 32位程序与64位程序在64位系统下调用Syscall的区别。


本文主要以SysWhispers2项目代码为代码讲解。

代码讲解:

1.通过PEB结构体获取NTDLL的基址,fs:[0x30]存储着PEB结构指针,PEB内存放着一个模块列表,通过模块链表判断模块名字可以获取到ntdll的模块地址:
PSW2_PEB Peb = (PSW2_PEB)__readfsdword(0x30);  PSW2_PEB_LDR_DATA Ldr = Peb->Ldr;  PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;  PVOID DllBase = NULL;    // Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second  // in the list, so it's safer to loop through the full list and find it.  PSW2_LDR_DATA_TABLE_ENTRY LdrEntry;  for (LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW2_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])  {      DllBase = LdrEntry->DllBase;      PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;      PIMAGE_NT_HEADERS NtHeaders = SW2_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);      PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;      DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;      if (VirtualAddress == 0) continue;        ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW2_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);        // If this is NTDLL.dll, exit loop.      PCHAR DllName = SW2_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);        if ((*(ULONG*)DllName | 0x20202020) != 'ldtn') continue;      if ((*(ULONG*)(DllName + 4) | 0x20202020) == 'ld.l') break;  }

PEB TEB结构图:

Hook示例及Syscall绕过


PEB结构体 Ldr存放着模块链表:

Hook示例及Syscall绕过


_PEB_LDR_DATA结构体,链表内存放着进程的模块信息:

Hook示例及Syscall绕过


_LIST_ENTRY 双向链表:

Hook示例及Syscall绕过


_LDR_DATA_TABLE_ENTRY 存放着dll信息,如名字、基址、大小等信息:

Hook示例及Syscall绕过


2.获取到ntdll基址后,解释PE文件格式结构找出函数导出表并筛选出导出函数筛选出Zw开头的函数,函数名字做一个哈希处理,录入数组
DWORD NumberOfNames = ExportDirectory->NumberOfNames;  PDWORD Functions = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);  PDWORD Names = SW2_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);  PWORD Ordinals = SW2_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);    // Populate SW2_SyscallList with unsorted Zw* entries.  DWORD i = 0;  PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;  do  {      PCHAR FunctionName = SW2_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);        // Is this a system call?      if (*(USHORT*)FunctionName == 'wZ')      {          Entries[i].Hash = SW2_HashSyscall(FunctionName);          Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];            i++;          if (i == SW2_MAX_ENTRIES) break;      }  } while (--NumberOfNames);

PE文件结构图:

Hook示例及Syscall绕过


CFF查看导出表函数:

Hook示例及Syscall绕过



3.函数调用号排序  
下图为不同系统 函数的System Call 序号,可以观察到不同的系统版本相同的函数序号是不一样的。这里面就会存在兼容性问题 ,系统版本不一,调用号不一样。

Hook示例及Syscall绕过


hell's Gate代码段:

// First opcodes should be ://    MOV R10, RCX//    MOV RCX, <syscall>if (*((PBYTE)pFunctionAddress + cw) == 0x4c && *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b && *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1 && *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8 && *((PBYTE)pFunctionAddress + 6 + cw) == 0x00 && *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) { BYTE high = *((PBYTE)pFunctionAddress + 5 + cw); BYTE low = *((PBYTE)pFunctionAddress + 4 + cw); pVxTableEntry->wSystemCall = (high << 8) | low; break;}

存在问题:当函数入口被hook,

MOV R10, RCX MOV RCX, <syscall>

发生变化,无法判断获取。

SysWhispers2代码段:

函数名字做一个哈希处理,录入数组,按系统调用地址排序,通过简单的冒泡算法,对代码地址进行升序排序,排序后的函数序号就是Syscall调用序号,不受hook影响,获取方法隐秘性强,并且适用于所有Windows版本。
// Sort the list by address in ascending order.  for (DWORD i = 0; i < SW2_SyscallList.Count - 1; i++)  {      for (DWORD j = 0; j < SW2_SyscallList.Count - i - 1; j++)      {          if (Entries[j].Address > Entries[j + 1].Address)          {              // Swap entries.              SW2_SYSCALL_ENTRY TempEntry;                TempEntry.Hash = Entries[j].Hash;              TempEntry.Address = Entries[j].Address;                Entries[j].Hash = Entries[j + 1].Hash;              Entries[j].Address = Entries[j + 1].Address;                Entries[j + 1].Hash = TempEntry.Hash;              Entries[j + 1].Address = TempEntry.Address;          }      }  }
4.32与64位程序在64位系统下兼容性问题
32位程序与32位系统不存在兼容性问题,32位程序与64位系统存在兼容性问题:
(1).windows/syswow64目录下的大量DLL库与SYSTEM32目录下的wow64.dll, wow64cpu.dll, wow64win.dll, ntdll.dll支撑着wow64机制
(2).Wow64下32位进程中实际有32位和64位两个逻辑子空间,每个子空间都 有各自的数据结构、堆栈,64位子空间负责与操作系统内核交互:
         32位用户态模式 <----->  64位用户态模式  <------->  64位内核
因此32位程序在32/64位系统下调用syscall会有区别,32位程序在32位系统下:
NtProtectVirtualMemory PROC push ebp mov ebp, esp push 001911B13h        ; Load function hash into ECX. call SW2_GetSyscallNumber  ;Number -> eax lea esp, [esp+4]     mov ecx, 5h      ;参数个数push_argument: dec ecx push [ebp + 08h + ecx * 4] jnz push_argument push ret_address_epilog ;ret address call do_sysenter_interupt lea esp, [esp+4]ret_address_epilog: mov esp, ebp pop ebp retdo_sysenter_interupt: mov edx, esp sysenter  retNtProtectVirtualMemory ENDP

32位程序在64位系统下:

NtProtectVirtualMemory PROC push ebp mov ebp, esp push 001911B13h        ; Load function hash into ECX. call SW2_GetSyscallNumber    ;Number -> eax lea esp, [esp+4] mov ecx, 5h          ;参数个数push_argument: dec ecx push [ebp + 08h + ecx * 4]  ;参数入栈 jnz push_argument push ret_address_epilog     ;ret address call dword ptr internal_cleancall_wow64_gate ; call KiFastSystemCall lea esp, [esp+4]ret_address_epilog: mov esp, ebp pop ebp ret

wow64_gate地址:fs:[0xC0]

internal_cleancall_wow64_gate = (void*)__readfsdword(0xC0);

示例通过使用syscall调用内存属性修改:

Hook示例及Syscall绕过


Hook示例及Syscall绕过


成功绕过hook:

Hook示例及Syscall绕过

实战绕过部分杀毒:

Hook示例及Syscall绕过

Hook示例及Syscall绕过

0x05 总结
Hook永远是攻防中最重要的一环,本文以简单的例子展示了一个hook例子与绕过示例。
站在攻击者角度Syscall技术虽然已经出来好几年,但是目前优秀的开源免杀框架无一例外都会使用Syscall技术,其重要性不言而喻。但随着AV/EDR厂商的检测力度加强,普通的Syscall已满足不了免杀需求,导致衍生出越来越多的syscall变种。但万变不离其宗,做到深入理解原理才能更好的去进行技术升级对抗。
站在防守者角度简单的一些hook技术已经无法捕获攻击者的动作行为,hook点应当在内核深层进行拦截捕获。


参考代码:

https://github.com/microsoft/Detours

https://github.com/am0nsec/HellsGate

https://github.com/jthuraisamy/SysWhispers2

https://github.com/klezVirus/SysWhispers3

https://github.com/mai1zhi2/ShellCodeFramework




Hook示例及Syscall绕过
END
Hook示例及Syscall绕过



Hook示例及Syscall绕过

长按扫描二维码

关注公众号

发送加群
可以一起交流哦


索隐实验室是长沙火线云网络科技有限公司的安全研究团队之一,索隐寓意发现隐秘的攻击。索隐实验室聚焦威胁检测与分析技术,包括入侵检测、溯源分析、自动化应急响应等方向。


原文始发于微信公众号(火线云安全研究团队):Hook示例及Syscall绕过

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月8日18:33:55
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Hook示例及Syscall绕过http://cn-sec.com/archives/1166986.html

发表评论

匿名网友 填写信息