首页
社区
课程
招聘
[原创][翻译]规避技术:操作系统特征
2021-5-21 10:06 7156

[原创][翻译]规避技术:操作系统特征

2021-5-21 10:06
7156

备注
原文地址:https://evasions.checkpoint.com/techniques/os-features.html
原文标题:Evasions: OS features
更新日期:2021年5月21日
此文后期:根据自身所学进行内容扩充
因自身技术有限,只能尽自身所能翻译国外技术文章,供大家学习,若有不当或可完善的地方,希望可以指出,用于共同完善这篇文章。


目录

  • 操作系统特征检测方法

  • 1. 检查调试权限

  • 2. 使用不平衡的堆栈

  • 反制措施

  • 归功于


操作系统特征检测方法
该组中的规避技术利用了操作系统的工作方式的特征性。
1. 检查调试权限
如果恶意软件在调试器或像Cuckoo这样的沙盒中运行,其进程令牌将有一个调试权限处于启用状态。发生这种情况是因为该权限在父进程中启用,并由恶意软件进程继承。

恶意软件试图用PROCESS_ALL_ACCESS访问权打开关键的系统进程,如csrss.exe、smss.exe、lsass.exe,然后试图终止它们。在正常情况下,当恶意软件从资源管理器或命令行执行时,这将失败,因为即使是管理员用户也不能终止这些进程。但是,如果进程令牌在启用状态下具有调试权限,这将取得成功。终止关键的系统进程会导致操作系统崩溃,出现0x000000F4的错误,所以伪装过程会被中止。
获取正在运行进程快照的函数:

  • CreateToolhelp32Snapshot

  • psapi.EnumProcesses (WinXP, Vista)

  • kernel32.EnumProcesses (Win7+)


用来启动进程的函数:

  • OpenProcess(PROCESS_ALL_ACCESS, ..., pid) // track for PIDs of 'csrss.exe', 'smss.exe', 'lsass.exe'


代码样本

/*
中文:
如果我们正在被调试,并且该进程有SeDebugPrivileges 权限,那么OpenProcess的调用将是成功的。
这需要管理员的权限!在Windows XP、Vista和7中,在调用OpenProcess时有 
PROCESS_ALL_ACCESS调用OpenProcess会失败,即使启用了SeDebugPrivilege。
这就是为什么我使用PROCESS_QUERY_LIMITED_INFORMATION。
English:
If we're being debugged and the process has SeDebugPrivileges 
privileges then OpenProcess call will be successful.
This requires administrator privilege!
In Windows XP, Vista and 7, calling OpenProcess with 
PROCESS_ALL_ACCESS will fait even with SeDebugPrivilege enabled,
That's why I used PROCESS_QUERY_LIMITED_INFORMATION
*/

DWORD GetCsrssProcessId()
{
  if (API::IsAvailable(API_IDENTIFIER::API_CsrGetProcessId))
  {
    auto CsrGetProcessId = static_cast<pCsrGetId>(API::GetAPI(API_IDENTIFIER::API_CsrGetProcessId));

    return CsrGetProcessId();
  }
  else
    return GetProcessIdFromName(_T("csrss.exe"));
}


BOOL CanOpenCsrss()
{
   HANDLE hCsrss = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, GetCsrssProcessId());
   if (hCsrss != NULL)
  {
    CloseHandle(hCsrss);
    return TRUE;
  }
  else
    return FALSE;
}


该代码样本的作者:al-khaser project
识别标志
如果OpenProcess在打开一个关键的系统进程时要求所有可能的权限--这是恶意软件试图应用这种规避技术的强烈迹象。
2. 使用不平衡的堆栈
这项技术是由Check Point恶意软件逆向工程团队在2016年病毒公告上提出的。它是由这个链接描述的。

为了跟踪进程行为,CuckooMon/Cuckoo Monitor模块钩住了相关功能。在这种类型的架构中,钩子在原始函数之前被调用。被钩住的函数除了使用原始函数的空间外,还可能使用堆栈上的一些空间。因此,被钩住的函数在堆栈上使用的总空间可能大于仅被原始函数使用的空间。

问题:恶意软件有关于被调用函数在堆栈上使用多少空间的信息。因此,它可以将堆栈指针移向较低的地址,其偏移量足以存储函数参数、局部变量和返回地址,为它们保留空间。恶意软件用一些相关数据填充堆栈指针下面的空间。然后,它将堆栈指针移到原来的位置并调用库函数。如果该函数没有被钩住,恶意软件会在相关数据之前填入保留的空间(见图1)。如果函数被钩住,恶意软件就会重叠相关数据,因为为原始函数的局部变量保留的空间小于钩子和原始函数的局部变量加起来的空间。因此,相关数据被破坏了(见图2)。如果它存储了一些函数的指针,这些指针在以后的执行过程中被使用,恶意软件就会跳转到任意代码,偶尔会使应用程序崩溃。
在非拦截函数调用中的堆栈:

在拦截函数调用中的堆栈:

解决方案:为了避免这种行为,Cuckoo Monitor/CuckooMon模块可以使用一个两阶段的钩子过程。在第一阶段,代替钩子的代码执行,它可以将堆栈指针移到一个特定大小的较低的地址,足以容纳恶意软件的相关数据。然后,函数的参数被复制到新的堆栈指针下。只有在这些准备工作完成后,第二阶段的钩子(执行真正的钩子)才被调用。恶意软件输入的相关数据驻留在上层堆栈地址,因此它不会以任何方式受到调用函数的影响。
代码样本

bool Cuckoo::CheckUnbalancedStack() const {
  usf_t f = {
    { lib_name_t(L"ntdll"), { 
      {sizeof(void *), NULL, "ZwDelayExecution", ARG_ITEM(kZwDelayExecutionArgs) }
    } }
  };
  const uint8_t canary[8] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF };

  uint32_t args_size;
  const void *args_buff;
  uint32_t reserved_size;
  uint32_t reserved_size_after_call;
  uint32_t canary_size;
  FARPROC func;
  bool us_detected;
  void *canary_addr = (void *)&canary[0];
  
  static_assert((sizeof(canary) % sizeof(void *)) == 0, "Invalid canary alignement");
  
  for (auto it = f.begin(), end = f.end(); it != end; ++it) {
    for (auto &vi : it->second) {
      vi.func_addr = GetProcAddress(GetModuleHandleW(it->first.c_str()), vi.func_name.c_str());

      // call to Unbalanced Stack
      args_size = vi.args_size;
      args_buff = vi.args_buff;
      canary_size = sizeof(canary);
      reserved_size = sizeof(void *) + vi.local_vars_size + canary_size;
      reserved_size_after_call = reserved_size + args_size;
      func = vi.func_addr;
      us_detected = false;

      __asm {
        pusha
        mov ecx, args_size
        sub esp, ecx
        mov esi, args_buff
        mov edi, esp
        cld
        rep movsb
        sub esp, reserved_size
        mov ecx, canary_size
        mov esi, canary_addr
        mov edi, esp
        rep movsb
        add esp, reserved_size
        mov eax, func
        call eax
        sub esp, reserved_size_after_call
        mov ecx, canary_size
        mov esi, canary_addr
        mov edi, esp
        repz cmpsb
        cmp ecx, 0
        setnz us_detected
        add esp, reserved_size_after_call
        popa
      }

      if (us_detected)
        return true;
    }
  }

  return false;  
}


识别标志
我们不提供识别标志,因为在恶意软件方面跟踪这种行为是非常棘手的。
反制措施

  • 与检查调试权限相比:拦截OpenProcess并跟踪关键系统进程的PID - 然后返回一个错误。

  • 与使用不平衡堆栈相比。1)在函数调用前调整堆栈;2)在内核模式下拦截。


归功于
归功于开源项目,代码样本来自该项目:


尽管Check Point工具InviZzzible已经实现了所有这些功能,但由于代码的模块化结构,需要更多的空间来展示这个工具的代码样本,以达到相同的目的。这就是为什么我们决定在整个百科全书中使用其他伟大的开源项目作为例子。



[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回