当前位置:   article > 正文

ark笔记_processselfdelete

processselfdelete

ark相关技术是关于进程检测,杀进程,线程检测,杀线程,怎么检测模块,隐藏dll等。还有驱动模块怎么检测,怎么卸载别人的驱动。SSDT,shadowSSDT,FSD相关。

进程

进程枚举

R3枚举进程

CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS)/Process32First/Process32Next,建一个快照,然后去遍历

ZwQuerySystemInformation(SystemProcessAndThreadsInformation),ntdll.dll里需要getprocdress导入然后用SystemProcessAndThreadsInformation功能号去查,返回系统中所有的进程和线程。如果是32位程序跑在64位系统可能存在截断指针,8->4的情况。

EnumWindows/GetWindowThreadProcessId,EnumWindows枚举窗口,得到句柄,GetWindowThreadProcessId然后得到窗口由哪个线程,进程创建。存在问题程序没有窗口。

for(;;)OpenProcess,通过循环for(int nPid=0;nPid<=2^26;nPid+=4)内部OpenProcess(nPid,....)。

R0枚举进程

第一种方法PspCidTable

查找PspCidTable

ExEnumHandleTable

解析PspCidTable,

HANDLE_TABLE

HANDLE_TABLE_ENTRY

KPCP.KdVersionBlock->PspCidTable(wdbgexts.h)

PspCidTable这是个想树一样的结构,在Win2000中,句柄表使用的是固定的3层索引模式,而在WinXp,2003以及之后的系统中使用的动态的可扩展的3层索引句柄表,存放的是系统中所有的进程和线程对象,其索引也就是进程ID(PID)或线程ID(TID)。我们每创建一个进程,系统就把我们的进程线程信息保存在这里。我们就遍历这个结构,找出所有的进程线程。

参考资料

https://blog.csdn.net/jiangfuqiang/article/details/4567625

PspCidTable使用了handle_table句柄表这种结构但是有所区别。因为windows对于这个结构是动态分配和确定层数,另外如何确定层数,就是如下,因为句柄表肯定都是按页或者对齐的。拿32位举例,其中低二位 TagBits 位被操作系统完全忽略掉,因此可以被应用程序自由改写。高 6 位,作为 Tag 来使用,其中最高位等于 1 ,代表是系统 HandleTable 中的 Handle。windows就是由最低两位空闲的位置来保存具体有几层。windows有很多这种用法。

下面用windbg观察下结构

  1. 1: kd> dq PspCidTable
  2. fffff800`04042bc8 fffff8a0`00004880 00000000`00000000
  3. fffff800`04042bd8 ffffffff`80000020 00000000`00000101
  4. fffff800`04042be8 ffffffff`800002e4 ffffffff`80000024
  5. fffff800`04042bf8 00000000`00000000 00000000`00000113
  6. fffff800`04042c08 00000000`00000000 00000000`00000000
  7. fffff800`04042c18 fffff800`03f9db40 00000000`00000000
  8. fffff800`04042c28 00000000`00000000 00000000`00000000
  9. fffff800`04042c38 00000000`00000000 00000000`00000008
  10. kd> dt _HANDLE_TABLE fffff8a0`00004880
  11. nt!_HANDLE_TABLE
  12. +0x000 TableCode : 0xfffff8a0`01254001
  13. +0x008 QuotaProcess : (null)
  14. +0x010 UniqueProcessId : (null)
  15. +0x018 HandleLock : _EX_PUSH_LOCK
  16. +0x020 HandleTableList : _LIST_ENTRY [ 0xfffff8a0`000048a0 - 0xfffff8a0`000048a0 ]
  17. +0x030 HandleContentionEvent : _EX_PUSH_LOCK
  18. +0x038 DebugInfo : (null)
  19. +0x040 ExtraInfoPages : 0n0
  20. +0x044 Flags : 1
  21. +0x044 StrictFIFO : 0y1
  22. +0x048 FirstFreeHandle : 0xe74
  23. +0x050 LastFreeHandleEntry : 0xfffff8a0`01aa21d0 _HANDLE_TABLE_ENTRY
  24. +0x058 HandleCount : 0x371
  25. +0x05c NextHandleNeedingPool : 0x1000
  26. +0x060 HandleCountHighWatermark : 0x372

得到TableCode 0xfffff8a0`01254001,最后一位是1,说明有2层。然后我们看一下第一层

  1. 1: kd> dq 0xfffff8a0`01254000
  2. fffff8a0`01254000 fffff8a0`00005000 fffff8a0`01255000
  3. fffff8a0`01254010 fffff8a0`01aa2000 fffff8a0`01df1000
  4. fffff8a0`01254020 00000000`00000000 00000000`00000000
  5. fffff8a0`01254030 00000000`00000000 00000000`00000000
  6. fffff8a0`01254040 00000000`00000000 00000000`00000000

 因为windows内存按页4k对齐,每个里面每一项都是一个指针,指向下一层。这里4个,这里只用了32字节。下面再看下一层叶子节点,对应的结构是HANDLE_TABLE_ENTRY

  1. 1: kd> dq fffff8a0`00005000
  2. fffff8a0`00005000 00000000`00000000 48202454`fffffffe
  3. fffff8a0`00005010 fffffa80`018fe041 00000000`00000000
  4. fffff8a0`00005020 fffffa80`018feb61 00000000`00000000
  5. fffff8a0`00005030 fffffa80`0196f041 00000000`00000000
  6. fffff8a0`00005040 fffffa80`0198b041 00000000`00000000
  7. fffff8a0`00005050 fffffa80`01929b61 00000000`00000000
  8. fffff8a0`00005060 fffffa80`0197db61 fffff880`00000000
  9. fffff8a0`00005070 fffffa80`019769e1 fffff880`00000000
  10. 1: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005000
  11. nt!_HANDLE_TABLE_ENTRY
  12. +0x000 Object : (null)
  13. +0x000 ObAttributes : 0
  14. +0x000 InfoTable : (null)
  15. +0x000 Value : 0
  16. +0x008 GrantedAccess : 0xfffffffe
  17. +0x008 GrantedAccessIndex : 0xfffe
  18. +0x00a CreatorBackTraceIndex : 0xffff
  19. +0x008 NextFreeTableEntry : 0xfffffffe

这个句柄对象是空值,可能是创建完释放掉了。HANDLE_TABLE_ENTRY每一项是64个字节。那我们看下这个页下一项。

  1. 1: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005000+0x10
  2. nt!_HANDLE_TABLE_ENTRY
  3. +0x000 Object : 0xfffffa80`018fe041 Void
  4. +0x000 ObAttributes : 0x18fe041
  5. +0x000 InfoTable : 0xfffffa80`018fe041 _HANDLE_TABLE_ENTRY_INFO
  6. +0x000 Value : 0xfffffa80`018fe041
  7. +0x008 GrantedAccess : 0
  8. +0x008 GrantedAccessIndex : 0
  9. +0x00a CreatorBackTraceIndex : 0
  10. +0x008 NextFreeTableEntry : 0

 我们要知道这个对象是进程还是线程,查询下,因为对象这些结构最后一位应该是0,所以查询时把末尾数字去掉

  1. 1: kd> !object 0xfffffa80`018fe040
  2. Object: fffffa80018fe040 Type: (fffffa80018fda00) Process
  3. ObjectHeader: fffffa80018fe010 (new version)
  4. HandleCount: 4 PointerCount: 157

 发现是个process

  1. 1: kd> dt _eprocess 0xfffffa80`018fe040
  2. nt!_EPROCESS
  3. +0x000 Pcb : _KPROCESS
  4. +0x160 ProcessLock : _EX_PUSH_LOCK
  5. +0x168 CreateTime : _LARGE_INTEGER 0x1d53011`4551bed7
  6. +0x170 ExitTime : _LARGE_INTEGER 0x0
  7. +0x178 RundownProtect : _EX_RUNDOWN_REF
  8. +0x180 UniqueProcessId : 0x00000000`00000004 Void
  9. +0x188 ActiveProcessLinks : _LIST_ENTRY [ 0xfffffa80`0279b498 - 0xfffff800`04042b90 ]
  10. +0x198 ProcessQuotaUsage : [2] 0
  11. +0x1a8 ProcessQuotaPeak : [2] 0
  12. +0x1b8 CommitCharge : 0x29
  13. +0x1c0 QuotaBlock : 0xfffff800`04020c00 _EPROCESS_QUOTA_BLOCK
  14. +0x1c8 CpuQuotaBlock : (null)
  15. +0x1d0 PeakVirtualSize : 0xb24000
  16. +0x1d8 VirtualSize : 0x5ad000
  17. +0x1e0 SessionProcessLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
  18. +0x1f0 DebugPort : (null)
  19. +0x1f8 ExceptionPortData : (null)
  20. +0x1f8 ExceptionPortValue : 0
  21. +0x1f8 ExceptionPortState : 0y000
  22. +0x200 ObjectTable : 0xfffff8a0`00001780 _HANDLE_TABLE
  23. +0x208 Token : _EX_FAST_REF
  24. +0x210 WorkingSetPage : 0
  25. +0x218 AddressCreationLock : _EX_PUSH_LOCK
  26. +0x220 RotateInProgress : (null)
  27. +0x228 ForkInProgress : (null)
  28. +0x230 HardwareTrigger : 0
  29. +0x238 PhysicalVadRoot : 0xfffffa80`01992500 _MM_AVL_TABLE
  30. +0x240 CloneRoot : (null)
  31. +0x248 NumberOfPrivatePages : 0xb
  32. +0x250 NumberOfLockedPages : 0x40
  33. +0x258 Win32Process : (null)
  34. +0x260 Job : (null)
  35. +0x268 SectionObject : (null)
  36. +0x270 SectionBaseAddress : (null)
  37. +0x278 Cookie : 0
  38. +0x27c UmsScheduledThreads : 0
  39. +0x280 WorkingSetWatch : (null)
  40. +0x288 Win32WindowStation : (null)
  41. +0x290 InheritedFromUniqueProcessId : (null)
  42. +0x298 LdtInformation : (null)
  43. +0x2a0 Spare : (null)
  44. +0x2a8 ConsoleHostProcess : 0
  45. +0x2b0 DeviceMap : 0xfffff8a0`00008bc0 Void
  46. +0x2b8 EtwDataSource : (null)
  47. +0x2c0 FreeTebHint : 0x000007ff`fffe0000 Void
  48. +0x2c8 FreeUmsTebHint : 0x00000000`772c9000 Void
  49. +0x2d0 PageDirectoryPte : _HARDWARE_PTE
  50. +0x2d0 Filler : 0
  51. +0x2d8 Session : (null)
  52. +0x2e0 ImageFileName : [15] "System"
  53. +0x2ef PriorityClass : 0x2 ''
  54. +0x2f0 JobLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
  55. +0x300 LockedPagesList : (null)
  56. +0x308 ThreadListHead : _LIST_ENTRY [ 0xfffffa80`018fef80 - 0xfffffa80`03dea460 ]
  57. +0x318 SecurityPort : (null)
  58. +0x320 Wow64Process : (null)
  59. +0x328 ActiveThreads : 0x6b
  60. +0x32c ImagePathHash : 0
  61. +0x330 DefaultHardErrorProcessing : 5
  62. +0x334 LastThreadExitStatus : 0n0
  63. +0x338 Peb : (null)
  64. +0x340 PrefetchTrace : _EX_FAST_REF
  65. +0x348 ReadOperationCount : _LARGE_INTEGER 0xf
  66. +0x350 WriteOperationCount : _LARGE_INTEGER 0x0
  67. +0x358 OtherOperationCount : _LARGE_INTEGER 0x1ea
  68. +0x360 ReadTransferCount : _LARGE_INTEGER 0x2509758
  69. +0x368 WriteTransferCount : _LARGE_INTEGER 0x0
  70. +0x370 OtherTransferCount : _LARGE_INTEGER 0x130b
  71. +0x378 CommitChargeLimit : 0
  72. +0x380 CommitChargePeak : 0x50
  73. +0x388 AweInfo : (null)
  74. +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
  75. +0x398 Vm : _MMSUPPORT
  76. +0x420 MmProcessLinks : _LIST_ENTRY [ 0xfffffa80`0279b730 - 0xfffff800`0401c5e0 ]
  77. +0x430 HighestUserAddress : (null)
  78. +0x438 ModifiedPageCount : 0x24ba
  79. +0x43c Flags2 : 0x2d800
  80. +0x43c JobNotReallyActive : 0y0
  81. +0x43c AccountingFolded : 0y0
  82. +0x43c NewProcessReported : 0y0
  83. +0x43c ExitProcessReported : 0y0
  84. +0x43c ReportCommitChanges : 0y0
  85. +0x43c LastReportMemory : 0y0
  86. +0x43c ReportPhysicalPageChanges : 0y0
  87. +0x43c HandleTableRundown : 0y0
  88. +0x43c NeedsHandleRundown : 0y0
  89. +0x43c RefTraceEnabled : 0y0
  90. +0x43c NumaAware : 0y0
  91. +0x43c ProtectedProcess : 0y1
  92. +0x43c DefaultPagePriority : 0y101
  93. +0x43c PrimaryTokenFrozen : 0y1
  94. +0x43c ProcessVerifierTarget : 0y0
  95. +0x43c StackRandomizationDisabled : 0y1
  96. +0x43c AffinityPermanent : 0y0
  97. +0x43c AffinityUpdateEnable : 0y0
  98. +0x43c PropagateNode : 0y0
  99. +0x43c ExplicitAffinity : 0y0
  100. +0x440 Flags : 0x14040800
  101. +0x440 CreateReported : 0y0
  102. +0x440 NoDebugInherit : 0y0
  103. +0x440 ProcessExiting : 0y0
  104. +0x440 ProcessDelete : 0y0
  105. +0x440 Wow64SplitPages : 0y0
  106. +0x440 VmDeleted : 0y0
  107. +0x440 OutswapEnabled : 0y0
  108. +0x440 Outswapped : 0y0
  109. +0x440 ForkFailed : 0y0
  110. +0x440 Wow64VaSpace4Gb : 0y0
  111. +0x440 AddressSpaceInitialized : 0y10
  112. +0x440 SetTimerResolution : 0y0
  113. +0x440 BreakOnTermination : 0y0
  114. +0x440 DeprioritizeViews : 0y0
  115. +0x440 WriteWatch : 0y0
  116. +0x440 ProcessInSession : 0y0
  117. +0x440 OverrideAddressSpace : 0y0
  118. +0x440 HasAddressSpace : 0y1
  119. +0x440 LaunchPrefetched : 0y0
  120. +0x440 InjectInpageErrors : 0y0
  121. +0x440 VmTopDown : 0y0
  122. +0x440 ImageNotifyDone : 0y0
  123. +0x440 PdeUpdateNeeded : 0y0
  124. +0x440 VdmAllowed : 0y0
  125. +0x440 CrossSessionCreate : 0y0
  126. +0x440 ProcessInserted : 0y1
  127. +0x440 DefaultIoPriority : 0y010
  128. +0x440 ProcessSelfDelete : 0y0
  129. +0x440 SetTimerResolutionLink : 0y0
  130. +0x444 ExitStatus : 0n259
  131. +0x448 VadRoot : _MM_AVL_TABLE
  132. +0x488 AlpcContext : _ALPC_PROCESS_CONTEXT
  133. +0x4a8 TimerResolutionLink : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
  134. +0x4b8 RequestedTimerResolution : 0
  135. +0x4bc ActiveThreadsHighWatermark : 0x6f
  136. +0x4c0 SmallestTimerResolution : 0
  137. +0x4c8 TimerResolutionStackRecord : (null)

这样我们就看到了这个进程详细信息,然后继续解析,就能知道这个进程的详细信息了。这个代码实际可以去逆向的ExpLookupHandleTableEntry,

  1. /************************************************************************
  2. * Name : APEnumProcessInfoByIterateThirdLevelHandleTable
  3. * Param: TableCode
  4. * Param: EProcess
  5. * Param: pti
  6. * Param: ThreadCount
  7. * Ret : VOID
  8. * 遍历三级表
  9. ************************************************************************/
  10. VOID
  11. APEnumProcessInfoByIterateThirdLevelHandleTable(IN UINT_PTR TableCode,
  12. OUT PPROCESS_INFORMATION pi, IN UINT32 ProcessCount)
  13. {
  14. do
  15. {
  16. APEnumProcessInfoByIterateSecondLevelHandleTable(TableCode, pi, ProcessCount);
  17. TableCode += sizeof(UINT_PTR);
  18. } while (*(PUINT_PTR)TableCode != 0 && MmIsAddressValid((PVOID)*(PUINT_PTR)TableCode));
  19. }
  20. NTSTATUS
  21. APEnumProcessInfoByIteratePspCidTable(OUT PPROCESS_INFORMATION pi, IN UINT32 ProcessCount)
  22. {
  23. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  24. // 保存之前的模式,转成KernelMode
  25. PETHREAD EThread = PsGetCurrentThread();
  26. UINT8 PreviousMode = APChangeThreadMode(EThread, KernelMode);
  27. UINT_PTR PspCidTable = APGetPspCidTableAddress();
  28. APChangeThreadMode(EThread, PreviousMode);
  29. pi->NumberOfProcesses = 1; // 先把0号成员预留出来,存放Idle
  30. // EnumHandleTable
  31. if (PspCidTable)
  32. {
  33. PHANDLE_TABLE HandleTable = NULL;
  34. HandleTable = (PHANDLE_TABLE)(*(PUINT_PTR)PspCidTable); // HandleTable = fffff8a0`00004910
  35. if (HandleTable && MmIsAddressValid((PVOID)HandleTable))
  36. {
  37. UINT8 TableLevel = 0; // 指示句柄表层数
  38. UINT_PTR TableCode = 0; // 地址存放句柄表首地址
  39. TableCode = HandleTable->TableCode & 0xFFFFFFFFFFFFFFFC; // TableCode = 0xfffff8a0`00fc5000
  40. TableLevel = HandleTable->TableCode & 0x03; // TableLevel = 0x01
  41. if (TableCode && MmIsAddressValid((PVOID)TableCode))
  42. {
  43. switch (TableLevel)
  44. {
  45. case 0:
  46. {
  47. // 一层表
  48. APEnumProcessInfoByIterateFirstLevelHandleTable(TableCode, pi, ProcessCount);
  49. break;
  50. }
  51. case 1:
  52. {
  53. // 二层表
  54. APEnumProcessInfoByIterateSecondLevelHandleTable(TableCode, pi, ProcessCount);
  55. break;
  56. }
  57. case 2:
  58. {
  59. // 三层表
  60. APEnumProcessInfoByIterateThirdLevelHandleTable(TableCode, pi, ProcessCount);
  61. break;
  62. }
  63. default:
  64. break;
  65. }
  66. }
  67. }
  68. }
  69. if (pi->NumberOfProcesses > 1)
  70. {
  71. // 填充Idle的信息
  72. pi->ProcessEntry[0].ProcessId = 0;
  73. pi->ProcessEntry[0].EProcess = (UINT_PTR)APGetPsIdleProcess(); // 全局导出
  74. pi->ProcessEntry[0].ParentProcessId = 0;
  75. Status = STATUS_SUCCESS;
  76. }
  77. DbgPrint("EnumProcessInfo by iterate PspCidTable\r\n");
  78. return Status;
  79. }

但是现在有个问题,如果在应用层打开文件,根据句柄,createFile/readFile,windows是不会让用户层看到这个FileObject,一般操作文件都是把句柄传进内核,SSDT存在大量句柄,内核层通过函数ObReferenceObjectByHandle(hFile,IoFileObjectType,&Fileobject),通过这个函数把用户层句柄值通过这个表找到对象转换成FileObject,然后用去操作文件。

那么这个句柄值被分成的字段,在32位如下

 

最低2位在句柄表示没用。(所以可以在其他地方用在标识有几层)

 

Low Level Index在叶子节点做索引,9位因为一页4K,一个handle_table_entry有8字节,所以范围是2^12/2^3=2^9。

Mid Level Index,是如果2,3层,是2层第一层或者3层中间层索引,10是因为一页4K,一个地址指针32位是4字节,2^12/2^2=2^10.

High Level Index,用来如果3层第一层索引,windows没有全部用掉高11位来标识句柄,只用了5位,最高6位它用。

所以这也是在应用层变量句柄for循环nPid<=2^26的原因,最高6位没用。另外循环附图+=4,是因为最后两位句柄表没用。

64位也跟这个一样,没有扩展,。因为64位指针变成8字节,然后handle_table_entry变成16字节,所以Low Level Index变成了8,Mid Level Index成了9,High Level Index变成了7,然后剩下的没用。

所以内核关于进程枚举步骤就是

查找PspCidTable(在ntoskrnl.exe中),未导出,有两种方法查找,1.反汇编PsLookupProcessByProcessId,这个函数就有对其引用。如下

  1. 1: kd> u PsLookupProcessByProcessId l 40
  2. nt!PsLookupProcessByProcessId:
  3. fffff800`0416e1fc 48895c2408 mov qword ptr [rsp+8],rbx
  4. fffff800`0416e201 48896c2410 mov qword ptr [rsp+10h],rbp
  5. fffff800`0416e206 4889742418 mov qword ptr [rsp+18h],rsi
  6. fffff800`0416e20b 57 push rdi
  7. fffff800`0416e20c 4154 push r12
  8. fffff800`0416e20e 4155 push r13
  9. fffff800`0416e210 4883ec20 sub rsp,20h
  10. fffff800`0416e214 65488b3c2588010000 mov rdi,qword ptr gs:[188h]
  11. fffff800`0416e21d 4533e4 xor r12d,r12d
  12. fffff800`0416e220 488bea mov rbp,rdx
  13. fffff800`0416e223 66ff8fc4010000 dec word ptr [rdi+1C4h]
  14. fffff800`0416e22a 498bdc mov rbx,r12
  15. fffff800`0416e22d 488bd1 mov rdx,rcx
  16. fffff800`0416e230 488b0d9149edff mov rcx,qword ptr [nt!PspCidTable (fffff800`04042bc8)]
  17. fffff800`0416e237 e834480200 call nt!ExMapHandleToPointer (fffff800`04192a70)
  18. fffff800`0416e23c 458d6c2401 lea r13d,[r12+1]

第二种方法就是KPCR.KdVersionBlock->PspCidTable(wdbgexts.h)。这种相对简单。

获取kpcr可以参考https://blog.csdn.net/hu3167343/article/details/7612595

  1. NTSTATUS
  2. DriverEntry(IN PDRIVER_OBJECT pDriverObj, IN PUNICODE_STRING pRegistryString)
  3. {
  4. NTSTATUS status = STATUS_SUCCESS;
  5. ULONG FSAddr;
  6. pDriverObj->DriverUnload = DriverUnload;
  7. KeSetSystemAffinityThread(1); //使当前线程运行在第一个处理器上,之所以这样每一个cpu都有个KPCR,但是系统不会为每一个CPU设置KdVersionBlock,只会对第一个cpu设置,这个跟系统引导有关,ntosknrl这个也有个InitSyStem,会对KdVersionBlock初始化,可以理解这里就是单核,还在初始化,如果不设置,获取到其他cpu这里就是0
  8. __asm{
  9. push eax
  10. mov eax, fs:[0x34] ;得到KdVersionBlock的地址
  11. add eax,18h ;得到指向PsLoadedModuleList的地址
  12. mov eax,[eax] ;得到PsLoadedModuleList的地址
  13. mov eax,[eax] ;取出PsLoadedModuleList里面的内容, 即KLDR_DATA_TABLE_ENTRY结构
  14. mov eax,[eax+18h] ;取出DllBase, 即ntoskrnl.exe的基地址
  15. mov FSAddr, eax
  16. pop eax
  17. }
  18. KeRevertToUserAffinityThread();//恢复线程运行的处理器
  19. KdPrint(("0x%08X\n", FSAddr));
  20. return STATUS_SUCCESS;
  21. }

mov eax, fs:[0x34] ;得到KdVersionBlock的地址(),63位就变成了GS段,然后再去找头文件wdbgexts.h。

但是这里有个问题,a.exe创建,记录在这个表里,然后a进程又退出了。退出时候肯定会从PspCidTable摘掉这个进程。但是在期间b进程调用链OpenProcess获取了a进程。这时候会出现一个问题,a进程由于被b打开,所以计数器不为0,所以此时退出并不能销毁自己。所以会造成僵尸进程。

如果不想自己解析就调用windowsapi,ExEnumHandleTable,回调函数里也有。

windows句柄表分配算法分析可以参考下面网页

https://bbs.pediy.com/thread-84827.htm

以上急速通过PspCidTable的方法,此外还可以

通过PsActiveProcessHead

这是一个链表,系统每创建一个进程,都会把进程放到PsActiveProcessHead,对这个双向链表做循环ActiveProcessLinks.Blink,就能遍历枚举所有进程。或者KPCR的KdVersionBlock->PsActiveProcessHead里也有。

所以病毒之类的要想隐藏自己。就可以根据Pid去遍历找到PspCidTable.handle_table_entry.Object这一项,然后指向NULL,还有就是remove掉PsActiveProcessHead自己,但是由于x64有PG,所以可能会蓝屏。

第三种方法是KiWaitInListHead/KiWaitOutListHead/KiDispatcherReadyListHead,

这个只能枚举一部分,前2者是链表,通过ETRHEAD.WaitListEntry串联(这些个链表是系统阻塞队列,存放着系统的一些线程通过API,IoThreadToProcess就能得到所属进程)。后者是在非2000系统是LIST_ENTRY[32],也是通过ETHERA.WaitListEntry串联(这个是等待队列)。

关于这3个表的找法,前两个表xp下是反汇编KeDelayExcution Thread,后面版本系统通过KeGetCurrentPrcb()->KiWaitIniListHead,后一个表在xp下是通过KiReadyThread->KiDispatcherReadyListHead,后面版本KeGetCurrentPrcb()->kiDispatcherReadyListHead。参考资料

https://blog.csdn.net/iiprogram/article/details/672558

关于病毒这方面隐藏,工作量就比较大了。

关于内核枚举驱动进程的第四种方法是

搜索内存

因为不管前面再这么隐藏,不可能抹去进程对象。所以我们可以搜索内存,找特征码,这里的问题就是提取特诊码,还有搜索范围。

关于搜索范围,32位只会有4G寻址空间,所以搜索0x800000到0xFFFFFFFF,64位地址空间是2^47,就不能简单for循环了。

在64位,win7是写死的,比如0xFFFFFA8000000000,在win8是因为这种进程对象是放在非分页内存,而非分页内存范围是有关键字MmIsNoPagedSystemAddressValid和MmNoPagedPoolStart这两个字段之间搜索。

win10里面既没写死,也没有关键字了。但是因为这些Object会被集中放置。所以我们可以在写DriverEntry是有drvObj,或者IoCtldevice会有自己进程上下文。这些都是放一起的,所以我们就以这个点为基准上下扫描。

第五种方法是

CSRSS.EXE句柄

原理是系统每创建一个进程或者线程,都会给CSRSS发一个LPC消息,告诉其创建进程,CSRSS就会去保存进程线程句柄,所以我们就想着找到CSRSS然后遍历里面的句柄。比如拿到进程句柄后,NtQuerySystemInformation,会返回handle和Object(eprocess).

操作就是csrss.exe识别。这个进程肯定有id,然后NtQuerySystemInformation,收集句柄对象。然后用Csrss过滤,然后筛选感兴趣的句柄。哪些是进程句柄,这时有两种思路,1.NtQuerySystemInformation有句柄类型代号,7是进程。如果不想不硬编码7,就是先看自己进程句柄的类型,然后比较是否一样也行。

然后根据句柄对应的object,拿到进程对象。然后就能搜集起来了。

这里面的问题比如CSRSS.exe句柄怎么找,就要要知道他的进程id,参考资料

https://www.2cto.com/article/201205/131961.html

原理就是通过GetInfoTable获取进程所有句柄。对每一个句柄分析。如果不是自己进程句柄就打开文件,然后复制一份回来再query。然后硬编码发现名字是L"\\Windows\\ApiPort",就认为所在进程就是CSRSS进程。因为每创建一个进程就会跟他自己的CSRSS通信,让CSRSS记录一份,通过LPC,所以会有一个ALPCPORT,在通信时肯定要send和get之类的,这时需要名字,名字就是Windows\\ApiPort。只有csrss会占用他。所以就可以了。

  1. HANDLE GetCsrPid()
  2. {
  3. HANDLE Process, hObject;
  4. HANDLE CsrId = (HANDLE)0;
  5. OBJECT_ATTRIBUTES obj;
  6. CLIENT_ID cid;
  7. UCHAR Buff[0x100];
  8. POBJECT_NAME_INFORMATION ObjName = (PVOID)&Buff;
  9. PSYSTEM_HANDLE_INFORMATION_EX Handles;
  10. ULONG r;
  11. Handles = GetInfoTable(SystemHandleInformation);
  12. if (!Handles) return CsrId;
  13. for (r = 0; r < Handles->NumberOfHandles; r++)
  14. {
  15. if (Handles->Information[r].ObjectTypeNumber == 21) //Port object
  16. {
  17. InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
  18. cid.UniqueProcess = (HANDLE)Handles->Information[r].ProcessId;
  19. cid.UniqueThread = 0;
  20. if (NT_SUCCESS(NtOpenProcess(&Process, PROCESS_DUP_HANDLE, &obj, &cid)))
  21. {
  22. if (NT_SUCCESS(ZwDuplicateObject(Process, (HANDLE)Handles->Information[r].Handle,NtCurrentProcess(), &hObject, 0, 0, DUPLICATE_SAME_ACCESS)))
  23. {
  24. if (NT_SUCCESS(ZwQueryObject(hObject, ObjectNameInformation, ObjName, 0x100, NULL)))
  25. {
  26. if (ObjName->Name.Buffer && !wcsncmp(L"\\Windows\\ApiPort", ObjName->Name.Buffer, 20))
  27. {
  28. CsrId = (HANDLE)Handles->Information[r].ProcessId;
  29. }
  30. }
  31. ZwClose(hObject);
  32. }
  33. ZwClose(Process);
  34. }
  35. }
  36. }
  37. ExFreePool(Handles);
  38. return CsrId;
  39. }

因为DriverEntry在的system进程并没有载入win32k.sys,所以,要访问shadowssdt表,必须KeStackAttackProces到一个有GUI线程的进程中,而csrss.exe就是这样的一个合适的进程,所以我们先attachProcess过去,切换cr3,最后再detach。然后在其中就能操作了。

那关于这个方法的anti就是让ARK工具看不见或者看见的不是实际的。在PTE做手脚,或者EPT hook,或者在ark工具的页表做pte hook,

其他方法还有交换上下文,因为进程在频繁切换,此时做inline hook收集。此时要注意寄存器,还有DPC级别。

RootKit在驱动层就是hookR3R0调用驱动检测的函数。

EPROCESS识别

前面做内存搜索,那么关于搜索的特征码怎么确定呢?

  1. nt!_EPROCESS
  2. +0x000 Pcb : _KPROCESS
  3. +0x160 ProcessLock : _EX_PUSH_LOCK
  4. +0x168 CreateTime : _LARGE_INTEGER//创建时间
  5. +0x170 ExitTime : _LARGE_INTEGER//已经消亡了,僵尸进程这里是个点
  6. +0x178 RundownProtect : _EX_RUNDOWN_REF//进程保护,类似个锁,可以防止进程被结束掉,比如attach别人进程先加个锁,然后detach再释放
  7. +0x180 UniqueProcessId : Ptr64 Void//pid
  8. +0x188 ActiveProcessLinks : _LIST_ENTRY
  9. +0x198 ProcessQuotaUsage : [2] Uint8B
  10. +0x1a8 ProcessQuotaPeak : [2] Uint8B
  11. +0x1b8 CommitCharge : Uint8B
  12. +0x1c0 QuotaBlock : Ptr64 _EPROCESS_QUOTA_BLOCK
  13. +0x1c8 CpuQuotaBlock : Ptr64 _PS_CPU_QUOTA_BLOCK
  14. +0x1d0 PeakVirtualSize : Uint8B
  15. +0x1d8 VirtualSize : Uint8B
  16. +0x1e0 SessionProcessLinks : _LIST_ENTRY//进程检测相关
  17. +0x1f0 DebugPort : Ptr64 Void
  18. +0x1f8 ExceptionPortData : Ptr64 Void
  19. +0x1f8 ExceptionPortValue : Uint8B
  20. +0x1f8 ExceptionPortState : Pos 0, 3 Bits
  21. +0x200 ObjectTable : Ptr64 _HANDLE_TABLE//进程句柄链表
  22. +0x208 Token : _EX_FAST_REF//令牌权限相关
  23. +0x210 WorkingSetPage : Uint8B
  24. +0x218 AddressCreationLock : _EX_PUSH_LOCK
  25. +0x220 RotateInProgress : Ptr64 _ETHREAD
  26. +0x228 ForkInProgress : Ptr64 _ETHREAD
  27. +0x230 HardwareTrigger : Uint8B
  28. +0x238 PhysicalVadRoot : Ptr64 _MM_AVL_TABLE//占用的物理内存
  29. +0x240 CloneRoot : Ptr64 Void
  30. +0x248 NumberOfPrivatePages : Uint8B
  31. +0x250 NumberOfLockedPages : Uint8B
  32. +0x258 Win32Process : Ptr64 Void
  33. +0x260 Job : Ptr64 _EJOB
  34. +0x268 SectionObject : Ptr64 Void//这个和下面可以获得进程对应的模块路径
  35. +0x270 SectionBaseAddress : Ptr64 Void
  36. +0x278 Cookie : Uint4B
  37. +0x27c UmsScheduledThreads : Uint4B
  38. +0x280 WorkingSetWatch : Ptr64 _PAGEFAULT_HISTORY
  39. +0x288 Win32WindowStation : Ptr64 Void
  40. +0x290 InheritedFromUniqueProcessId : Ptr64 Void
  41. +0x298 LdtInformation : Ptr64 Void
  42. +0x2a0 Spare : Ptr64 Void
  43. +0x2a8 ConsoleHostProcess : Uint8B
  44. +0x2b0 DeviceMap : Ptr64 Void
  45. +0x2b8 EtwDataSource : Ptr64 Void
  46. +0x2c0 FreeTebHint : Ptr64 Void
  47. +0x2c8 FreeUmsTebHint : Ptr64 Void
  48. +0x2d0 PageDirectoryPte : _HARDWARE_PTE
  49. +0x2d0 Filler : Uint8B
  50. +0x2d8 Session : Ptr64 Void
  51. +0x2e0 ImageFileName : [15] UChar
  52. +0x2ef PriorityClass : UChar
  53. +0x2f0 JobLinks : _LIST_ENTRY
  54. +0x300 LockedPagesList : Ptr64 Void
  55. +0x308 ThreadListHead : _LIST_ENTRY
  56. +0x318 SecurityPort : Ptr64 Void//32位指向一个peb64
  57. +0x320 Wow64Process : Ptr64 Void
  58. +0x328 ActiveThreads : Uint4B
  59. +0x32c ImagePathHash : Uint4B
  60. +0x330 DefaultHardErrorProcessing : Uint4B
  61. +0x334 LastThreadExitStatus : Int4B
  62. +0x338 Peb : Ptr64 _PEB
  63. +0x340 PrefetchTrace : _EX_FAST_REF
  64. +0x348 ReadOperationCount : _LARGE_INTEGER
  65. +0x350 WriteOperationCount : _LARGE_INTEGER
  66. +0x358 OtherOperationCount : _LARGE_INTEGER
  67. +0x360 ReadTransferCount : _LARGE_INTEGER
  68. +0x368 WriteTransferCount : _LARGE_INTEGER
  69. +0x370 OtherTransferCount : _LARGE_INTEGER
  70. +0x378 CommitChargeLimit : Uint8B
  71. +0x380 CommitChargePeak : Uint8B
  72. +0x388 AweInfo : Ptr64 Void
  73. +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
  74. +0x398 Vm : _MMSUPPORT
  75. +0x420 MmProcessLinks : _LIST_ENTRY
  76. +0x430 HighestUserAddress : Ptr64 Void
  77. +0x438 ModifiedPageCount : Uint4B
  78. +0x43c Flags2 : Uint4B
  79. +0x43c JobNotReallyActive : Pos 0, 1 Bit
  80. +0x43c AccountingFolded : Pos 1, 1 Bit
  81. +0x43c NewProcessReported : Pos 2, 1 Bit
  82. +0x43c ExitProcessReported : Pos 3, 1 Bit
  83. +0x43c ReportCommitChanges : Pos 4, 1 Bit
  84. +0x43c LastReportMemory : Pos 5, 1 Bit
  85. +0x43c ReportPhysicalPageChanges : Pos 6, 1 Bit
  86. +0x43c HandleTableRundown : Pos 7, 1 Bit
  87. +0x43c NeedsHandleRundown : Pos 8, 1 Bit
  88. +0x43c RefTraceEnabled : Pos 9, 1 Bit
  89. +0x43c NumaAware : Pos 10, 1 Bit
  90. +0x43c ProtectedProcess : Pos 11, 1 Bit
  91. +0x43c DefaultPagePriority : Pos 12, 3 Bits
  92. +0x43c PrimaryTokenFrozen : Pos 15, 1 Bit
  93. +0x43c ProcessVerifierTarget : Pos 16, 1 Bit
  94. +0x43c StackRandomizationDisabled : Pos 17, 1 Bit
  95. +0x43c AffinityPermanent : Pos 18, 1 Bit
  96. +0x43c AffinityUpdateEnable : Pos 19, 1 Bit
  97. +0x43c PropagateNode : Pos 20, 1 Bit
  98. +0x43c ExplicitAffinity : Pos 21, 1 Bit
  99. +0x440 Flags : Uint4B
  100. +0x440 CreateReported : Pos 0, 1 Bit
  101. +0x440 NoDebugInherit : Pos 1, 1 Bit
  102. +0x440 ProcessExiting : Pos 2, 1 Bit
  103. +0x440 ProcessDelete : Pos 3, 1 Bit
  104. +0x440 Wow64SplitPages : Pos 4, 1 Bit
  105. +0x440 VmDeleted : Pos 5, 1 Bit
  106. +0x440 OutswapEnabled : Pos 6, 1 Bit
  107. +0x440 Outswapped : Pos 7, 1 Bit
  108. +0x440 ForkFailed : Pos 8, 1 Bit
  109. +0x440 Wow64VaSpace4Gb : Pos 9, 1 Bit
  110. +0x440 AddressSpaceInitialized : Pos 10, 2 Bits
  111. +0x440 SetTimerResolution : Pos 12, 1 Bit
  112. +0x440 BreakOnTermination : Pos 13, 1 Bit
  113. +0x440 DeprioritizeViews : Pos 14, 1 Bit
  114. +0x440 WriteWatch : Pos 15, 1 Bit
  115. +0x440 ProcessInSession : Pos 16, 1 Bit
  116. +0x440 OverrideAddressSpace : Pos 17, 1 Bit
  117. +0x440 HasAddressSpace : Pos 18, 1 Bit
  118. +0x440 LaunchPrefetched : Pos 19, 1 Bit
  119. +0x440 InjectInpageErrors : Pos 20, 1 Bit
  120. +0x440 VmTopDown : Pos 21, 1 Bit
  121. +0x440 ImageNotifyDone : Pos 22, 1 Bit
  122. +0x440 PdeUpdateNeeded : Pos 23, 1 Bit
  123. +0x440 VdmAllowed : Pos 24, 1 Bit
  124. +0x440 CrossSessionCreate : Pos 25, 1 Bit
  125. +0x440 ProcessInserted : Pos 26, 1 Bit
  126. +0x440 DefaultIoPriority : Pos 27, 3 Bits
  127. +0x440 ProcessSelfDelete : Pos 30, 1 Bit
  128. +0x440 SetTimerResolutionLink : Pos 31, 1 Bit
  129. +0x444 ExitStatus : Int4B
  130. +0x448 VadRoot : _MM_AVL_TABLE
  131. +0x488 AlpcContext : _ALPC_PROCESS_CONTEXT
  132. +0x4a8 TimerResolutionLink : _LIST_ENTRY
  133. +0x4b8 RequestedTimerResolution : Uint4B
  134. +0x4bc ActiveThreadsHighWatermark : Uint4B
  135. +0x4c0 SmallestTimerResolution : Uint4B
  136. +0x4c8 TimerResolutionStackRecord : Ptr64 _PO_DIAG_STACK_RECORD

首先观察eprocess结构,找到一些固定的点。不能找可以随意改的地方比如进程UniqueProcessId 被篡改,那我们再想打开这个进程,还是传打开前的id,因为函数调用方面,OpenProcess(40)->NtOpenProcess(40)->PsLookupProcessByProcessId(40,eprocess)。

所以可以通过下面几个地方作为入口去提取特征。

ObjectType(Address,固定ID,随机固定值)

PEB

VADRoot,记录了进程占用了哪些虚拟内存块,通过VirtualAlloc申请的。是一颗平衡二叉树。

ObjectTable,是进程句柄表,类似一颗树。

几个ThreadListHead,以为进程肯定是有线程的。这个线程就是在PEB里。

结合ETHREDA信息。

其中ObjectType是对象的类型,可以通过_eprocess-sizeof(OBJECT_HEADER)

  1. 1: kd> dt _OBJECT_HEADER
  2. nt!_OBJECT_HEADER
  3. +0x000 PointerCount : Int8B
  4. +0x008 HandleCount : Int8B
  5. +0x008 NextToFree : Ptr64 Void
  6. +0x010 Lock : _EX_PUSH_LOCK
  7. +0x018 TypeIndex : UChar
  8. +0x019 TraceFlags : UChar
  9. +0x01a InfoMask : UChar
  10. +0x01b Flags : UChar
  11. +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
  12. +0x020 QuotaBlockCharged : Ptr64 Void
  13. +0x028 SecurityDescriptor : Ptr64 Void
  14. +0x030 Body : _QUAD

这个就是OBJECT_HEADER,Body就是根据不同对象而不同,这里就是eprocess。TypeIndex 就是对象类型,xp里面指向对象类型,什么PsProcessType的结构体。win7是硬编码,win10之后都把他编码了,动态变化的。过的话哦就是伪造对象类型。

结束进程

结束进程可以通过

NtTerminateProcess

涂改内存(应用层,写成00或cc,就会出异常,关了)

卸载模块(FreeLibrary(ntdll.dll))

窗口攻击

CreateJobObject/AssignProcessToJobObject/TerminateJobObject或设置关闭时结束,就是把进程关联到工作集,然后结束工作集。

注入代码,ExitProcess

SetThreadContext,设置线程指向非法内存

调试器吸附,退出

对每个线程(GetNextProcessThread)PspTerminateThreadByPointer.

线程

线程相关的操作跟进程一样,也是涉及,线程枚举,ETHREAD识别,TerminateThread

线程枚举

线程枚举总体来说有

PspCidTable

hook SwapContext

搜索内存

ThreadListEntry,因为枚举某个进程的线程,已经知道进程对象,可以遍历EPROCESS的ThreadListEntry,或者KPROCESS的ThreadLIstEntry

ETHREAD识别

跟EPROCESS类似,可以通过

ObjectType

TEB(但是比如system线程内核态,没有)

ETHREAD.ThreadListHead

KTHREAD.ThreadListHead

定位EPROCESS,检测EPROCESS的一些信息

结束线程

NtTerminateThread

PspTerminateThreadByPointer(未导出,需要反汇编上面的函数找到)

Insert APC杀线程

https://bbs.pediy.com/thread-59091-1.htm

进程模块

PEB枚举进程模块

https://www.cnblogs.com/dsky/archive/2012/02/23/2364503.html

就是进程会记录在,PEB里,有个LDR,系统给每个模块分配一个_LDR_DATA_TABLE_ENTRY,通过链表连在一起。其中结构入下

  1. 1: kd> dt _PEB
  2. nt!_PEB
  3. +0x000 InheritedAddressSpace : UChar
  4. +0x001 ReadImageFileExecOptions : UChar
  5. +0x002 BeingDebugged : UChar
  6. +0x003 BitField : UChar
  7. +0x003 ImageUsesLargePages : Pos 0, 1 Bit
  8. +0x003 IsProtectedProcess : Pos 1, 1 Bit
  9. +0x003 IsLegacyProcess : Pos 2, 1 Bit
  10. +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
  11. +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
  12. +0x003 SpareBits : Pos 5, 3 Bits
  13. +0x008 Mutant : Ptr64 Void
  14. +0x010 ImageBaseAddress : Ptr64 Void
  15. +0x018 Ldr : Ptr64 _PEB_LDR_DATA

里面的LDR

  1. 1: kd> dt _PEB_LDR_DATA
  2. nt!_PEB_LDR_DATA
  3. +0x000 Length : Uint4B
  4. +0x004 Initialized : UChar
  5. +0x008 SsHandle : Ptr64 Void
  6. +0x010 InLoadOrderModuleList : _LIST_ENTRY
  7. +0x020 InMemoryOrderModuleList : _LIST_ENTRY
  8. +0x030 InInitializationOrderModuleList : _LIST_ENTRY
  9. +0x040 EntryInProgress : Ptr64 Void
  10. +0x048 ShutdownInProgress : UChar
  11. +0x050 ShutdownThreadId : Ptr64 Void

这里面InLoadOrderModuleList指向下面的InLoadOrderLinks,InMemoryOrderModuleList指向 下面的InMemoryOrderLinks,InMemoryOrderModuleList指向下面的InInitializationOrderLinks

  1. 1: kd> dt _LDR_DATA_TABLE_ENTRY
  2. nt!_LDR_DATA_TABLE_ENTRY
  3. +0x000 InLoadOrderLinks : _LIST_ENTRY
  4. +0x010 InMemoryOrderLinks : _LIST_ENTRY
  5. +0x020 InInitializationOrderLinks : _LIST_ENTRY
  6. +0x030 DllBase : Ptr64 Void
  7. +0x038 EntryPoint : Ptr64 Void
  8. +0x040 SizeOfImage : Uint4B
  9. +0x048 FullDllName : _UNICODE_STRING
  10. +0x058 BaseDllName : _UNICODE_STRING
  11. +0x068 Flags : Uint4B
  12. +0x06c LoadCount : Uint2B
  13. +0x06e TlsIndex : Uint2B
  14. +0x070 HashLinks : _LIST_ENTRY
  15. +0x070 SectionPointer : Ptr64 Void
  16. +0x078 CheckSum : Uint4B
  17. +0x080 TimeDateStamp : Uint4B
  18. +0x080 LoadedImports : Ptr64 Void
  19. +0x088 EntryPointActivationContext : Ptr64 _ACTIVATION_CONTEXT
  20. +0x090 PatchInformation : Ptr64 Void
  21. +0x098 ForwarderLinks : _LIST_ENTRY
  22. +0x0a8 ServiceTagLinks : _LIST_ENTRY
  23. +0x0b8 StaticLinks : _LIST_ENTRY
  24. +0x0c8 ContextInformation : Ptr64 Void
  25. +0x0d0 OriginalBase : Uint8B
  26. +0x0d8 LoadTime : _LARGE_INTEGER

64位下稍微复杂些

分两种情况

1.64位程序在64位系统运行,这些模块都会记录到PEB,可以通过EPROCESS里面。

2.32位程序运行在64位系统运行。会有两个ntdll.dll32位和64位模块。windows会把64模块保存在ProcessPEB,32位保存在EPROCESS::wow64process。

有个注意点,32位程序运行在64位OS下

32位peb(wow64process)的ntdll.dll在LDR_DTAT_TABLE_ENTRY里的全路径和64位在这里记录的一样,这里就有问题到底哪个是真的。实际上32位的需要重定位到syswow64(Wow64EnableWow64FsRedirection)

所以上面的隐藏方法就是把自己从上面3个链表摘掉。

LdrpHashTable枚举进程模块(LIST_ENTRY[32])

这里也跟上面枚举PEB一样,也要分32位系统和64位运行32位和64位程序的情况。这个链表是_LDR_DATA_TABLE_ENTRY的 +0x070 HashLinks连在一起的。

Vad枚举进程模块

ZwQueryVirtualMemory(MemorySectionName)
 

for(void *p=0;pM<0x7FFFF...;p+=0x1000){ZwQueryVirtualMemory(hprocess,p,MemorySectionName,ppName);}

https://bbs.pediy.com/thread-66886.htm

搜索内存枚举进程模块

搜索范围

PE识别

模块卸载

远线程FreeLibrary

NtUnmapViewOfSection

进程其他信息

进程快捷键枚举

驱动模块

枚举

PsLoadMuduleList

ObjectDirectory

PsLoadMuduleList

驱动模块加载,都会保存到一个全局链表,PsLoadMuduleList,非导出,通过LDR_DATA_TABLE_ENTRY的第一项InLoadOrderLinks链接在一起,然后可以遍历这个链表得到所有。

KPCR.KdVersionBlock->PsLoadModuleList.

另外在DriverEntry里DriverObject的DriverSection就是LDR_DATA_TABLE_ENTRY,PsLoadModuleList的加载顺序就是ntoskrnl然后其他。

ObjectDirectory,对象目录,驱动会创建对象,通过驱动对象或者设备对象得到驱动模块,所有驱动都会保存在对象树里。

  1. 1: kd> !object \\
  2. Object: fffff8a0000040d0 Type: (fffffa8001848240) Directory
  3. ObjectHeader: fffff8a0000040a0 (new version)
  4. HandleCount: 0 PointerCount: 42
  5. Directory Object: 00000000 Name: \
  6. Hash Address Type Name
  7. ---- ------- ---- ----
  8. 01 fffff8a0000068f0 Directory ObjectTypes
  9. 05 fffff8a0003f92d0 SymbolicLink SystemRoot
  10. 06 fffff8a00013fc50 Directory Sessions
  11. 07 fffffa80036c8090 ALPC Port MmcssApiPort
  12. 08 fffff8a00000c260 Directory ArcName
  13. 09 fffff8a000071d30 Directory NLS
  14. 10 fffffa80036e2c00 ALPC Port ThemeApiPort
  15. fffff8a0001321e0 Directory Windows
  16. fffffa80038c4660 Event LanmanServerAnnounceEvent
  17. fffff8a000008060 Directory GLOBAL??
  18. 11 fffff8a000139080 Directory RPC Control
  19. 13 fffffa8002f757a0 Event EFSInitEvent
  20. 14 fffffa8001d62480 Device clfs
  21. fffff8a0002edde0 SymbolicLink Dfs
  22. 15 fffffa800279cb80 ALPC Port SeRmCommandPort
  23. fffffa80026b9860 Event CsrSbSyncEvent
  24. 16 fffff8a000004370 SymbolicLink DosDevices
  25. 17 fffff8a005ebb2e0 Directory KnownDlls32
  26. 18 fffff8a00001aca0 Key \REGISTRY
  27. 19 fffff8a003261c90 Directory BaseNamedObjects
  28. 20 fffffa80018f0830 ALPC Port PowerPort
  29. 21 fffffa8003640bd0 ALPC Port SmSsWinStationApiPort
  30. fffffa800276af20 Event UniqueInteractiveSessionIdEvent
  31. fffff8a00006f390 Directory UMDFCommunicationPorts
  32. 22 fffff8a00396cbd0 Directory KnownDlls
  33. fffffa80018f0140 ALPC Port PowerMonitorPort
  34. 23 fffff8a000006eb0 Directory KernelObjects
  35. fffff8a00006f060 Directory FileSystem
  36. fffffa8001d5c520 Device Ntfs
  37. 26 fffff8a000006060 Directory Callback
  38. fffffa8003579090 ALPC Port SeLsaCommandPort
  39. 28 fffff8a00000b060 Directory Security
  40. 29 fffffa800371d6a0 ALPC Port UxSmsApiPort
  41. 30 fffff8a000010920 Directory Device
  42. 32 fffffa80035a7260 Event DSYSDBG.Debug.Trace.Memory.218
  43. 34 fffffa800279f610 ALPC Port SmApiPort
  44. fffff8a000f87080 Section LsaPerformance
  45. fffffa80027a4510 Event UniqueSessionIdEvent
  46. 36 fffff8a000071b80 Directory Driver
  47. fffffa800359b2b0 Event SAM_SERVICE_STARTED

我们要找到就是\driver或者\device,如果是驱动就可以找到驱动对象,如果是设备,可以找到属于哪个驱动,然后去找driversection。

这里面有个特殊的_object_directory

利用IoDriver(Device)ObjectType::TypeList

就是每个object_head里有个objectType的TypeIndex,里面有个Typelist,当标志位致1就会插入到这个表,但是系统没用,或者在object_head的前面还有一个结构。

暴力搜索DriverObject

https://bbs.pediy.com/thread-109819.htm

驱动模块卸载

常规是调用ZwUnloadDriver。必须要有服务名。如果没有服务名就要自己实现。就是想办法直接传driverobject去系统空间调用DriverUnload。

SSDT HOOK检测

PE loader

我们检测比如ssdt有没有被inlinehook,就需要比对文件,要把检测模块从当前内存dump出来,我们要跟

文件的作对比。我们要把文件中的这份加载到内存比较。所以需要PE loader

但要注意比较的时候可能不同,比如一些数据全局变量,加壳的都是动态的,所以这些都要注意。比如如果是全局变量引起的不同,可以通过节比较,不整个比较,节里面比如不可写的节一个一个字节比较是可以,但如果节可写,就要注意了。

所以我们可以检测PE头,如果变化过多说明可能误报。

还有如果代码自修改。也是需要注意,所以一定要合理设计检测策略。

首先导出函数名收集可以从ntdll里面导出表里有。序号可以从导出表导出函数函数地址指向的第二个操作数获取(32位),64位是第二个函数第二个操作数,但是有个函数ZWQuerySystemTime没法收集需要单独硬编码。原因是,在内核中有个数据结构

_KUSER_SHARED_DATA的SystemTime在应用层就能读取,所以64位改了实现。

获取干净的ssdt表,可以从ntdll.dll的重定位表里收集。或者4字节扫看哪个不在这个模块。

获取正在使用的ssdt表,通过X86下KeServiceDescriptorTable,x64(SSDT为了节省空间还是一个4字节,距离ssdt表头的距离,在左移4位,低4位是参数个数)是在

SHADOWSSDT HOOK检测

函数名收集因为没有导出,通过win32.sys加载ida去找,然后硬编码了。一般都在.data开始,或者第四个字节都是BF。

干净的Shadow SSDT表获取,x86data段。x64导出了,通过W32pServiceTable。

https://www.cnblogs.com/Jesses/articles/1647674.html

FSD检测

驱动派发函数的检测,分为

函数名

当前函数

原始函数

比对

当前函数对象名获取通过打开当前对象。原始函数地址获取通过反汇编引擎。或者自己load这个ntfs.sys,load之后重定向,对某些函数进行导入(导入个假的之类的让堆栈平衡为了绕过检测代码)或者挂钩子,之后创建一个线程,自己调start函数,传的就是自己伪造的DriverObject,然后比对是否hook了。

页表的解析

判断虚拟地址是否合法。解析都是cpu硬件完成的。

32位模式下,PDE和PTE,如果非PAE,非扩展分页,就是通过cr3,然后101012去找。如果申请4M内存,会有1024个页面。会在第二级pte里面有1024个。这样是4K,所以intel为了省内存,使用只用一级PDE,没有二级,然后里面有一位进行标志LARGEPAGE,标志只有2级寻址。就是扩展分页模式。这个大页面是2^10*2^12=4M

关于PAE物理地址扩展模式就是变成29912分页。扩展分页模式。这个大页面是2^9*2^12=2m

64位模式下,使用4个页表,PML4,PDPE,PDE,PTE,9,9,9,9,12

页就时4k,大页2M大小。AMD大页面是把后面在合一个页表,大页就是1G。

高清这个原理可以自己实现一个MmIsAddressValid

  1. int PTESize;
  2. UINT_PTR PAGE_SIZE_LARGE;
  3. UINT_PTR MAX_PDE_POS;
  4. UINT_PTR MAX_PTE_POS;
  5. struct PTEStruct
  6. {
  7. unsigned P : 1; // present (1 = present)是否存在
  8. unsigned RW : 1; // read/write
  9. unsigned US : 1; // user/supervisor
  10. unsigned PWT : 1; // page-level write-through
  11. unsigned PCD : 1; // page-level cache disabled
  12. unsigned A : 1; // accessed
  13. unsigned Reserved : 1; // dirty
  14. unsigned PS : 1; // page size (0 = 4-KB page)
  15. unsigned G : 1; // global page
  16. unsigned A1 : 1; // available 1 aka copy-on-write
  17. unsigned A2 : 1; // available 2/ is 1 when paged to disk
  18. unsigned A3 : 1; // available 3
  19. unsigned PFN : 20; // page-frame number
  20. };
  21. void InitMemSafe()
  22. {
  23. #ifndef AMD64
  24. ULONG cr4reg;
  25. //determine if PAE is used
  26. cr4reg=(ULONG)__readcr4();
  27. if ((cr4reg & 0x20)==0x20)
  28. {
  29. PTESize=8; //pae
  30. PAGE_SIZE_LARGE=0x200000;
  31. MAX_PDE_POS=0xC0604000;
  32. MAX_PTE_POS=0xC07FFFF8;
  33. }
  34. else
  35. {
  36. PTESize=4;
  37. PAGE_SIZE_LARGE=0x400000;
  38. MAX_PDE_POS=0xC0301000;
  39. MAX_PTE_POS=0xC03FFFFC;
  40. }
  41. #else
  42. PTESize=8; //pae
  43. PAGE_SIZE_LARGE=0x200000;
  44. MAX_PTE_POS=0xFFFFF6FFFFFFFFF8ULL;
  45. MAX_PDE_POS=0xFFFFF6FB7FFFFFF8ULL;
  46. #endif
  47. }
  48. BOOLEAN IsAddressSafe(UINT_PTR StartAddress)
  49. {
  50. #ifdef AMD64
  51. //cannonical check. Bits 48 to 63 must match bit 47
  52. UINT_PTR toppart=(StartAddress >> 47);
  53. if (toppart & 1)
  54. {
  55. //toppart must be 0x1ffff
  56. if (toppart != 0x1ffff)
  57. return FALSE;
  58. }
  59. else
  60. {
  61. //toppart must be 0
  62. if (toppart != 0)
  63. return FALSE;
  64. }
  65. #endif
  66. //PDT+PTE judge
  67. {
  68. #ifdef AMD64
  69. UINT_PTR kernelbase=0x7fffffffffffffffULL;
  70. if (StartAddress<kernelbase)
  71. {
  72. return TRUE;
  73. }
  74. else
  75. {
  76. PHYSICAL_ADDRESS physical;
  77. physical.QuadPart=0;
  78. physical=MmGetPhysicalAddress((PVOID)StartAddress);
  79. return (physical.QuadPart!=0);
  80. }
  81. return TRUE; //for now untill I ave figure out the win 4 paging scheme
  82. #else
  83. ULONG kernelbase=0x7ffe0000;
  84. UINT_PTR PTE,PDE;
  85. struct PTEStruct *x;
  86. if (StartAddress<kernelbase)
  87. {
  88. return TRUE;
  89. }
  90. PTE=(UINT_PTR)StartAddress;
  91. PTE=PTE/0x1000*PTESize+0xc0000000;
  92. //now check if the address in PTE is valid by checking the page table directory at 0xc0300000 (same location as CR3 btw)
  93. PDE=PTE/0x1000*PTESize+0xc0000000; //same formula
  94. x=(struct PTEStruct *)PDE;
  95. if ((x->P==0) && (x->A2==0))
  96. {
  97. //Not present or paged, and since paging in this area isn't such a smart thing to do just skip it
  98. //perhaps this is only for the 4 mb pages, but those should never be paged out, so it should be 1
  99. //bah, I've got no idea what this is used for
  100. return FALSE;
  101. }
  102. if (x->PS==1)
  103. {
  104. //This is a 4 MB page (no pte list)
  105. //so, (startaddress/0x400000*0x400000) till ((startaddress/0x400000*0x400000)+(0x400000-1) ) ) is specified by this page
  106. }
  107. else //if it's not a 4 MB page then check the PTE
  108. {
  109. //still here so the page table directory agreed that it is a usable page table entry
  110. x=(PVOID)PTE;
  111. if ((x->P==0) && (x->A2==0))
  112. return FALSE; //see for explenation the part of the PDE
  113. }
  114. return TRUE;
  115. #endif
  116. }
  117. }

禁止功能

比如创建进程。线程这些x86可以通过HOOK,64位通过PsSetCreateProcessNorityRountine()


{nPid,NtOpenProcess() NtTerminateProcess}

禁止创建进程,

FltRegisterFilter

自我保护

通过hook那些修改的函数。

Win7 X64 ShadowSSDT hook

;知道shadow ssdt serviceTable[] ULONG

自己函数放不进去,不是一个模块,所以先去win32k.sys找间隙(nop),要有八个字节,放我们hook函数地址,再要5个字节,进行jmp到我们放地址的地方。

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/333913
推荐阅读
相关标签
  

闽ICP备14008679号