[翻译]了解/检测Inline Hooks/ WinAPI Hooks(Ring3)

2021-04-22 17:35

阅读:349

    有没有想过恶意软件如何能够从Web浏览器中获取凭据?最流行的方法是Man-in-The-Browser(MITB)攻击,是我们熟知的Inline Hooking (有时又称为detours)。Inline hooks特别的通用并且在恶意软件中很常见。使用Inline Hooks,恶意软件可以控制任何进程的流程,它操纵着进程,恶意软件作者可以为所欲为。让我们来看看他们是如何做到这一点。

How do you get your code into the target process?

    Inline Hooks的第一步是让你的代码到目标进程中运行。这个过程被称为注入

    有几种常见的注入方法,例如CreateRemoteThread/RtlCreateUserThread/QueueUserApc/SetWindowsHookEx/SetThreadContext/反射注入/atom注入等等。在我们的例子中,我们使用  VirtualAllocEx 在Firefox中分配一个存储区域  0x0D000000。 CreateRemoteThread 然后将用于对Firefox内的  0x0D000000 可执行区域进行挂钩和初始化工作。(实际注入过程是对自己的一个复杂的话题,不在本文的讨论范围内。)下面是我们注入的代码块的样子。

技术分享图片

    Inline Hooks有四个部分组成:hook,shellcode(这里用的NOP填充),trample和返回。

What is a Windows API?

    这是一组函数和数据的结构,一个Windows程序可以用它来让Windows做一些事情,如打开一个文件,显示消息等。

一个Windows程序几乎所有的操作都调用各种AP??I函数。

    总的来说,所有的Windows使可用的API函数被称作“在Windows API”。

技术分享图片

The hook

    Hook有时被称为 trampoline,类似于一个交通员将交通转移到另一个位置。在我们的例子中,我们将挂钩WS2_32的 send() 函数。下面是ws2_32!send 最初的几个指令的为了成功挂钩,我们要覆盖调send()函数存在的一些指令。下面绿色方框的指令我们会将它覆盖掉

技术分享图片

    请注意,五个字节将要被改写。这是需要我们挂钩的确切字节数:一个  jmp 是跳进我们的代码指令  0x0D000000(还有其他的方法来放置钩子比如用  push 地址,  ret 指令组合。)挂钩之后 send() 函数如下图所示:

技术分享图片

The malicious code

    现在,执行流程已经被重定向到我们注入的代码,寄存器必须保存,以确保我们回到send()函数执行的时候不会crash在32位的过程中,我们可以使用PUSHAD指令将所有的寄存器保存下来当shellcode被执行完毕,在使用  POPAD 指令恢复所有寄存器的值,回到  send() 最初执行的时候。现在,我们在控制  send() 功能,我们必须想出一些有趣的事情。有一件事我们可以做的是,将一个发送POST请求和发送数据发回到我们,解析看看是否包含登录凭据。

 

The execution of trampled bytes and the return

    我们已经在shellcode中做了一些有用的东西之后,我们必须确保我们的程序返回到它在hook之前完全相同的状态。首先,我们使用的  popad 指令来恢复所有的寄存器。还记得我们Hook覆盖的五个字节吗?这些指令仍需要运行,因此他们在Hook的时候复制到我们shellcode的结尾,让他们在恢复寄存器后无缝的运行。最后,我们安全的调回到send()+ 0×05(我们的挂钩指令的长度)。

技术分享图片

Putting it all together

    让我们重新看一遍全部的流程。下面箭头是Firefox里面恢复send()钩子之后正常的执行流程技术分享图片

    注入我们的detour后,执行的流程入下图所示

技术分享图片

Reasons for hooking API calls

你可以想出任何的目的去挂钩每个API。这里有几个:

  • urlmon!URLDownloadToFile - 用于拦截下载的文件。挂钩全局的该函数来防止下载新的恶意软件。
  • ws2_32!send - 用于捕获未加密的流量POST凭据。
  • GetHostByName - 挂钩该函数为了忽略指定命令和控制站点的流量。
  • ws2_32!recv - 用于捕获未加密的流量传入数据包的数据。
  • Advapi32!CryptEncrypt -用于捕获被加密以前的数据,因为send()之后的数据不会是纯文本 
  • Advapi32!CryptDecrypt -用于解密从recv()来的数据。
  • User32!GetMessage - 用来拦截鼠标点击消息从虚拟键盘中点击图片。
  • Kernel32!ExitProcess - 防止进程结束自身(绕过游戏anticheats)

Detection

    这里有一个简单的解决方案的伪代码,如果攻击者是一个harduser就不是非常有效。这下面的代码将检查ExitProcess的开头预0xE9作比较(操作码JMP)

FARPROC Address = GetProcAddress(GetModuleHandle("kernel32.dll"),"ExitProcess");
if (*(BYTE*)Address == 0xE9)
{
 printf("Api hooked\n");//Do your thing
}

 

 


评论


亲,登录后才可以留言!