分类

链接

2013 年 11 月
 123
45678910
11121314151617
18192021222324
252627282930  

近期文章

热门标签

新人福利,免费薅羊毛

现在位置:    首页 > Others > 正文
共享办公室出租
VB下的隐藏进程检测
Others 暂无评论 阅读(1,557)

想必有些人听说过RootKit这种东西,它能隐藏自身或者别的进程/文件/注册表项等等。

听起来很可怕不是,比较新的灰鸽子就有这些功能。

想必也有人听说过 IceSword,大名鼎鼎的 Anti-RootKit 工具。灰鸽子隐藏的东西在它的检测下是绝对逃不过的。

那么杀灰鸽子必须用 IceSword 么?答案是否定的。事实上,灰鸽子是一款技术含量极低的 RootKit (它甚至不配叫这个名字)。

它的技术含量低到什么程度呢?用 VB 只需写上几行代码即可干掉灰鸽子。

相信么?不信吧……Let's Go!

用某些技术手段可以看到,灰鸽子将一个或两个 DLL(视版本不同数量有所不同)注入至系统所有进程,这个/些 DLL 用来实现 API Hook。

API Hook 是什么?抱歉,限于篇幅,具体定义请自行 Google。

它 Hook 的 API 之一是未公开的 NtQuerySystemInformation,用于隐藏其进程。
用户模式下枚举进程,无论是 ToolHelpAPI 系列,还是 PSAPI 系列,都会最终调用到这个 API。
所以灰鸽子 Hook 了这个 API,当发现正在枚举进程时,它会检查当前返回的结果是否为自己的进程,如果是的话直接忽略并返回下一个进程。
于是所有普通的进程管理工具(任务管理器、Process Explorer 等)中都不会出现其进程。

那么有什么办法可以检出其隐藏进程呢?IceSword 的方法是直接加载驱动程序进入内核,无视其 Hook(很拽吧?),并通过直接读取未公开内核链表 PspCidTable 枚举出所有线程,进而得到所有进程。

如果你对系统还算熟悉,你会了解到每个进程都对应了一个 PID(Process ID,进程标识符),其实就是一串数字。它是随机产生的,且如果没有特殊情况的话在整个系统里是唯一的。

你还会了解到如果要对其他进程进行操作的话,需要调用一个API:OpenProcess。它由 Kernel32.dll 导出。

The  code:
以文本方式查看复制代码打印关于程序
  1. Declare Function OpenProcess Lib "kernel32.dll" ( _
  2.          ByVal dwDesiredAccess As Long, _
  3.          ByVal bInheritHandle As Long, _
  4.          ByVal dwProcessId As Long) As Long

其中第一个参数为你想获得的访问权限(也就是你得到的句柄的权限)。
第二个参数指定句柄是否可继承,无关紧要。
第三个参数是关键,它指定了你想打开的进程的PID。

实际上,这个 API 将参数略作处理后就直接呼叫了内核 API:NtOpenProcess。

The  code:
以文本方式查看复制代码打印关于程序
  1. NTSTATUS NtOpenProcess (
  2. OUT PHANDLE ProcessHandle,
  3. IN ACCESS_MASK DesiredAccess,
  4. IN POBJECT_ATTRIBUTES ObjectAttributes,
  5. IN PCLIENT_ID ClientId OPTIONAL);

我们看看这个API究竟做了什么。

The  code:
以文本方式查看复制代码打印关于程序
  1. PAGE:0049D59E ; NTSTATUS __stdcall NtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
  2.                                                  POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
  3. PAGE:0049D59E                 public NtOpenProcess
  4. PAGE:0049D59E NtOpenProcess   proc near
  5. PAGE:0049D59E
  6. PAGE:0049D59E ProcessHandle   = dword ptr  4
  7. PAGE:0049D59E DesiredAccess   = dword ptr  8
  8. PAGE:0049D59E ObjectAttributes= dword ptr  0Ch
  9. PAGE:0049D59E ClientId        = dword ptr  10h
  10. PAGE:0049D59E
  11. PAGE:0049D59E                 push    0C4h
  12. PAGE:0049D5A3                 push    offset dword_413560 ; int
  13. PAGE:0049D5A8                 call    sub_40BA92
  14. PAGE:0049D5AD                 xor     esi, esi
  15. PAGE:0049D5AF                 mov     [ebp-2Ch], esi
  16. PAGE:0049D5B2                 xor     eax, eax
  17. PAGE:0049D5B4                 lea     edi, [ebp-28h]
  18. PAGE:0049D5B7                 stosd
  19. PAGE:0049D5B8                 mov     eax, large fs:124h
  20. PAGE:0049D5BE                 mov     al, [eax+140h]
  21. PAGE:0049D5C4                 mov     [ebp-34h], al
  22. PAGE:0049D5C7                 test    al, al
  23. PAGE:0049D5C9                 jz      loc_4BE034
  24. PAGE:0049D5CF                 mov     [ebp-4], esi
  25. PAGE:0049D5D2                 mov     eax, MmUserProbeAddress
  26. PAGE:0049D5D7                 mov     ecx, [ebp+8]
  27. PAGE:0049D5DA                 cmp     ecx, eax
  28. PAGE:0049D5DC                 jnb     loc_520CDE
  29. PAGE:0049D5E2 loc_49D5E2:
  30. PAGE:0049D5E2                 mov     eax, [ecx]
  31. PAGE:0049D5E4                 mov     [ecx], eax
  32. PAGE:0049D5E6                 mov     ebx, [ebp+10h]
  33. PAGE:0049D5E9                 test    bl, 3
  34. PAGE:0049D5EC                 jnz     loc_520CE5
  35. PAGE:0049D5F2 loc_49D5F2:
  36. PAGE:0049D5F2                 mov     eax, MmUserProbeAddress
  37. PAGE:0049D5F7                 cmp     ebx, eax
  38. PAGE:0049D5F9                 jnb     loc_520CEF
  39. PAGE:0049D5FF loc_49D5FF:
  40. PAGE:0049D5FF                 cmp     [ebx+8], esi
  41. PAGE:0049D602                 setnz   byte ptr [ebp-1Ah]
  42. PAGE:0049D606                 mov     ecx, [ebx+0Ch]
  43. PAGE:0049D609                 mov     [ebp-38h], ecx
  44. PAGE:0049D60C                 mov     ecx, [ebp+14h]
  45. PAGE:0049D60F                 cmp     ecx, esi
  46. PAGE:0049D611                 jz      loc_4CCB88
  47. PAGE:0049D617                 test    cl, 3
  48. PAGE:0049D61A                 jnz     loc_520CFB
  49. PAGE:0049D620 loc_49D620:
  50. PAGE:0049D620                 cmp     ecx, eax
  51. PAGE:0049D622                 jnb     loc_520D0D
  52. PAGE:0049D628 loc_49D628:
  53. PAGE:0049D628                 mov     eax, [ecx]
  54. PAGE:0049D62A                 mov     [ebp-2Ch], eax
  55. PAGE:0049D62D                 mov     eax, [ecx+4]
  56. PAGE:0049D630                 mov     [ebp-28h], eax
  57. PAGE:0049D633                 mov     byte ptr [ebp-19h], 1
  58. PAGE:0049D637 loc_49D637:
  59. PAGE:0049D637                 or      dword ptr [ebp-4], 0FFFFFFFFh
  60. PAGE:0049D63B loc_49D63B:
  61. PAGE:0049D63B
  62. PAGE:0049D63B                 cmp     byte ptr [ebp-1Ah], 0
  63. PAGE:0049D63F                 jnz     loc_520D34
  64. PAGE:0049D645 loc_49D645:
  65. PAGE:0049D645                 mov     eax, PsProcessType
  66. PAGE:0049D64A                 add     eax, 68h
  67. PAGE:0049D64D                 push    eax
  68. PAGE:0049D64E                 push    dword ptr [ebp+0Ch]
  69. PAGE:0049D651                 lea     eax, [ebp-0D4h]
  70. PAGE:0049D657                 push    eax
  71. PAGE:0049D658                 lea     eax, [ebp-0B8h]
  72. PAGE:0049D65E                 push    eax
  73. PAGE:0049D65F                 call    SeCreateAccessState
  74. PAGE:0049D664                 cmp     eax, esi
  75. PAGE:0049D666                 jl      loc_49D718
  76. PAGE:0049D66C                 push    dword ptr [ebp-34h] ; PreviousMode
  77. PAGE:0049D66F                 push    ds:stru_5B6978.HighPart
  78. PAGE:0049D675                 push    ds:stru_5B6978.LowPart ; PrivilegeValue
  79. PAGE:0049D67B                 call    SeSinglePrivilegeCheck
  80. PAGE:0049D680                 test    al, al
  81. PAGE:0049D682                 jnz     loc_4AA7DB
  82. PAGE:0049D688 loc_49D688:
  83. PAGE:0049D688                 cmp     byte ptr [ebp-1Ah], 0
  84. PAGE:0049D68C                 jnz     loc_520D52
  85. PAGE:0049D692                 cmp     byte ptr [ebp-19h], 0
  86. PAGE:0049D696                 jz      loc_4CCB9A
  87. PAGE:0049D69C                 mov     [ebp-30h], esi
  88. PAGE:0049D69F                 cmp     [ebp-28h], esi
  89. PAGE:0049D6A2                 jnz     loc_4C1301
  90. PAGE:0049D6A8                 lea     eax, [ebp-24h]
  91. PAGE:0049D6AB                 push    eax
  92. PAGE:0049D6AC                 push    dword ptr [ebp-2Ch]
  93. PAGE:0049D6AF                 call    PsLookupProcessByProcessId
  94. PAGE:0049D6B4 loc_49D6B4:

不要被吓倒,这个NtOpenProcess对参数进行检查(主要是检查进程权限)和处理后就传递给了另一个内核API:PsLookupProcessByProcessId。
那么我们继续追踪下去。

The  code:
以文本方式查看复制代码打印关于程序
  1. PAGE:0049D725                 public PsLookupProcessByProcessId
  2. PAGE:0049D725 PsLookupProcessByProcessId proc near
  3. PAGE:0049D725
  4. PAGE:0049D725
  5. PAGE:0049D725 ProcessId       = dword ptr  8
  6. PAGE:0049D725 Process         = dword ptr  0Ch
  7. PAGE:0049D725
  8. PAGE:0049D725                 mov     edi, edi
  9. PAGE:0049D727                 push    ebp
  10. PAGE:0049D728                 mov     ebp, esp
  11. PAGE:0049D72A                 push    ebx
  12. PAGE:0049D72B                 push    esi
  13. PAGE:0049D72C                 mov     eax, large fs:124h
  14. PAGE:0049D732                 push    [ebp+ProcessId]
  15. PAGE:0049D735                 mov     esi, eax
  16. PAGE:0049D737                 dec     dword ptr [esi+0D4h]
  17. PAGE:0049D73D                 push    PspCidTable
  18. PAGE:0049D743                 call    ExMapHandleToPointer
  19. PAGE:0049D748                 mov     ebx, eax
  20. PAGE:0049D74A                 test    ebx, ebx
  21. PAGE:0049D74C                 mov     [ebp+ProcessId], STATUS_INVALID_PARAMETER
  22. PAGE:0049D753                 jz      short loc_49D787
  23. PAGE:0049D755                 push    edi
  24. PAGE:0049D756                 mov     edi, [ebx]
  25. PAGE:0049D758                 cmp     byte ptr [edi], 3
  26. PAGE:0049D75B                 jnz     short loc_49D77A
  27. PAGE:0049D75D                 cmp     dword ptr [edi+1A4h], 0
  28. PAGE:0049D764                 jz      short loc_49D77A
  29. PAGE:0049D766                 mov     ecx, edi
  30. PAGE:0049D768                 call    sub_4134A9
  31. PAGE:0049D76D                 test    al, al
  32. PAGE:0049D76F                 jz      short loc_49D77A
  33. PAGE:0049D771                 mov     eax, [ebp+Process]
  34. PAGE:0049D774                 and     [ebp+ProcessId], 0
  35. PAGE:0049D778                 mov     [eax], edi
  36. PAGE:0049D77A loc_49D77A:
  37. PAGE:0049D77A                 push    ebx
  38. PAGE:0049D77B                 push    PspCidTable
  39. PAGE:0049D781                 call    ExUnlockHandleTableEntry
  40. PAGE:0049D786                 pop     edi
  41. PAGE:0049D787 loc_49D787:
  42. PAGE:0049D787                 inc     dword ptr [esi+0D4h]
  43. PAGE:0049D78D                 jnz     short loc_49D79A
  44. PAGE:0049D78F                 lea     eax, [esi+34h]
  45. PAGE:0049D792                 cmp     [eax], eax
  46. PAGE:0049D794                 jnz     loc_52388A
  47. PAGE:0049D79A loc_49D79A:
  48. PAGE:0049D79A                 mov     eax, [ebp+ProcessId]
  49. PAGE:0049D79D                 pop     esi
  50. PAGE:0049D79E                 pop     ebx
  51. PAGE:0049D79F                 pop     ebp
  52. PAGE:0049D7A0                 retn    8

PAGE:0049D73D                 push    PspCidTable
PAGE:0049D743                 call    ExMapHandleToPointer

这两个最为关键。你看到什么了?没错,PspCidTable。这个 API 用另外几个内核 API 遍历 PspCidTable 以确定一个进程是否存在。还记得 IceSword 是怎么检测隐藏进程的么(不过,最新版不只是检查了这个表)?

那么,回溯到文章开头,OpenProcess。你想到了什么?

The  code:
以文本方式查看复制代码打印关于程序
  1. Option Explicit
  2. Private Declare Function OpenProcess _
  3.                 Lib "kernel32.dll" (ByVal dwDesiredAccess As Long, _
  4.                                     ByVal bInheritHandle As Long, _
  5.                                     ByVal dwProcessId As Long) As Long
  6. Private Declare Function CloseHandle _
  7.                 Lib "kernel32.dll" (ByVal hObject As Long) As Long
  8. Private Const PROCESS_QUERY_INFORMATION As Long = (&H400)
  9. Private Const PROCESS_TERMINATE As Long = (&H1)
  10. Sub Main()
  11.     Dim i As Long
  12.     Debug.Print "VB Hidden Process Detector"
  13.     For i = 0 To 65536 Step 4
  14.         If OpenProcess(PROCESS_QUERY_INFORMATION, False, i) <> 0 Then
  15.             Debug.Print "PID=" & i
  16.             CloseHandle i
  17.         End If
  18.     Next
  19. End Sub

打开“立即”窗口,运行一下看看结果吧。
至 于怎么干掉灰鸽子,很简单,把检测到的隐藏进程的句柄传递给 TerminateProcess 这个 API 即可。不过之前 OpenProcess 的时候访问权限要换成 PROCESS_TERMINATE。一旦其主进程被结束,其隐藏手段也就完全失效,然后就可以可以用普通工具直接看到其文件等。

本文仅用于科普,技术稍好点的 RootKit 可轻松突破此方法。

本文借用了国外部分研究资料,在此向作者表示感谢。

谢谢观赏。

THE END

版权所有(C)FlowerCode[0GiNr]

============ 欢迎各位老板打赏~ ===========

本文版权归Bruce's Blog所有,转载引用请完整注明以下信息:
本文作者:Bruce
本文地址:VB下的隐藏进程检测 | Bruce's Blog

发表评论

留言无头像?