ark笔记

85 篇文章 6 订阅
83 篇文章 10 订阅

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: kd> dq PspCidTable
fffff800`04042bc8  fffff8a0`00004880 00000000`00000000
fffff800`04042bd8  ffffffff`80000020 00000000`00000101
fffff800`04042be8  ffffffff`800002e4 ffffffff`80000024
fffff800`04042bf8  00000000`00000000 00000000`00000113
fffff800`04042c08  00000000`00000000 00000000`00000000
fffff800`04042c18  fffff800`03f9db40 00000000`00000000
fffff800`04042c28  00000000`00000000 00000000`00000000
fffff800`04042c38  00000000`00000000 00000000`00000008
kd> dt _HANDLE_TABLE fffff8a0`00004880
nt!_HANDLE_TABLE
   +0x000 TableCode        : 0xfffff8a0`01254001
   +0x008 QuotaProcess     : (null) 
   +0x010 UniqueProcessId  : (null) 
   +0x018 HandleLock       : _EX_PUSH_LOCK
   +0x020 HandleTableList  : _LIST_ENTRY [ 0xfffff8a0`000048a0 - 0xfffff8a0`000048a0 ]
   +0x030 HandleContentionEvent : _EX_PUSH_LOCK
   +0x038 DebugInfo        : (null) 
   +0x040 ExtraInfoPages   : 0n0
   +0x044 Flags            : 1
   +0x044 StrictFIFO       : 0y1
   +0x048 FirstFreeHandle  : 0xe74
   +0x050 LastFreeHandleEntry : 0xfffff8a0`01aa21d0 _HANDLE_TABLE_ENTRY
   +0x058 HandleCount      : 0x371
   +0x05c NextHandleNeedingPool : 0x1000
   +0x060 HandleCountHighWatermark : 0x372

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

1: kd> dq 0xfffff8a0`01254000
fffff8a0`01254000  fffff8a0`00005000 fffff8a0`01255000
fffff8a0`01254010  fffff8a0`01aa2000 fffff8a0`01df1000
fffff8a0`01254020  00000000`00000000 00000000`00000000
fffff8a0`01254030  00000000`00000000 00000000`00000000
fffff8a0`01254040  00000000`00000000 00000000`00000000

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

1: kd> dq fffff8a0`00005000
fffff8a0`00005000  00000000`00000000 48202454`fffffffe
fffff8a0`00005010  fffffa80`018fe041 00000000`00000000
fffff8a0`00005020  fffffa80`018feb61 00000000`00000000
fffff8a0`00005030  fffffa80`0196f041 00000000`00000000
fffff8a0`00005040  fffffa80`0198b041 00000000`00000000
fffff8a0`00005050  fffffa80`01929b61 00000000`00000000
fffff8a0`00005060  fffffa80`0197db61 fffff880`00000000
fffff8a0`00005070  fffffa80`019769e1 fffff880`00000000
1: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005000
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : (null) 
   +0x000 ObAttributes     : 0
   +0x000 InfoTable        : (null) 
   +0x000 Value            : 0
   +0x008 GrantedAccess    : 0xfffffffe
   +0x008 GrantedAccessIndex : 0xfffe
   +0x00a CreatorBackTraceIndex : 0xffff
   +0x008 NextFreeTableEntry : 0xfffffffe

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

1: kd> dt _HANDLE_TABLE_ENTRY fffff8a0`00005000+0x10
nt!_HANDLE_TABLE_ENTRY
   +0x000 Object           : 0xfffffa80`018fe041 Void
   +0x000 ObAttributes     : 0x18fe041
   +0x000 InfoTable        : 0xfffffa80`018fe041 _HANDLE_TABLE_ENTRY_INFO
   +0x000 Value            : 0xfffffa80`018fe041
   +0x008 GrantedAccess    : 0
   +0x008 GrantedAccessIndex : 0
   +0x00a CreatorBackTraceIndex : 0
   +0x008 NextFreeTableEntry : 0

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

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

 发现是个process

1: kd> dt _eprocess 0xfffffa80`018fe040
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x160 ProcessLock      : _EX_PUSH_LOCK
   +0x168 CreateTime       : _LARGE_INTEGER 0x1d53011`4551bed7
   +0x170 ExitTime         : _LARGE_INTEGER 0x0
   +0x178 RundownProtect   : _EX_RUNDOWN_REF
   +0x180 UniqueProcessId  : 0x00000000`00000004 Void
   +0x188 ActiveProcessLinks : _LIST_ENTRY [ 0xfffffa80`0279b498 - 0xfffff800`04042b90 ]
   +0x198 ProcessQuotaUsage : [2] 0
   +0x1a8 ProcessQuotaPeak : [2] 0
   +0x1b8 CommitCharge     : 0x29
   +0x1c0 QuotaBlock       : 0xfffff800`04020c00 _EPROCESS_QUOTA_BLOCK
   +0x1c8 CpuQuotaBlock    : (null) 
   +0x1d0 PeakVirtualSize  : 0xb24000
   +0x1d8 VirtualSize      : 0x5ad000
   +0x1e0 SessionProcessLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
   +0x1f0 DebugPort        : (null) 
   +0x1f8 ExceptionPortData : (null) 
   +0x1f8 ExceptionPortValue : 0
   +0x1f8 ExceptionPortState : 0y000
   +0x200 ObjectTable      : 0xfffff8a0`00001780 _HANDLE_TABLE
   +0x208 Token            : _EX_FAST_REF
   +0x210 WorkingSetPage   : 0
   +0x218 AddressCreationLock : _EX_PUSH_LOCK
   +0x220 RotateInProgress : (null) 
   +0x228 ForkInProgress   : (null) 
   +0x230 HardwareTrigger  : 0
   +0x238 PhysicalVadRoot  : 0xfffffa80`01992500 _MM_AVL_TABLE
   +0x240 CloneRoot        : (null) 
   +0x248 NumberOfPrivatePages : 0xb
   +0x250 NumberOfLockedPages : 0x40
   +0x258 Win32Process     : (null) 
   +0x260 Job              : (null) 
   +0x268 SectionObject    : (null) 
   +0x270 SectionBaseAddress : (null) 
   +0x278 Cookie           : 0
   +0x27c UmsScheduledThreads : 0
   +0x280 WorkingSetWatch  : (null) 
   +0x288 Win32WindowStation : (null) 
   +0x290 InheritedFromUniqueProcessId : (null) 
   +0x298 LdtInformation   : (null) 
   +0x2a0 Spare            : (null) 
   +0x2a8 ConsoleHostProcess : 0
   +0x2b0 DeviceMap        : 0xfffff8a0`00008bc0 Void
   +0x2b8 EtwDataSource    : (null) 
   +0x2c0 FreeTebHint      : 0x000007ff`fffe0000 Void
   +0x2c8 FreeUmsTebHint   : 0x00000000`772c9000 Void
   +0x2d0 PageDirectoryPte : _HARDWARE_PTE
   +0x2d0 Filler           : 0
   +0x2d8 Session          : (null) 
   +0x2e0 ImageFileName    : [15]  "System"
   +0x2ef PriorityClass    : 0x2 ''
   +0x2f0 JobLinks         : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
   +0x300 LockedPagesList  : (null) 
   +0x308 ThreadListHead   : _LIST_ENTRY [ 0xfffffa80`018fef80 - 0xfffffa80`03dea460 ]
   +0x318 SecurityPort     : (null) 
   +0x320 Wow64Process     : (null) 
   +0x328 ActiveThreads    : 0x6b
   +0x32c ImagePathHash    : 0
   +0x330 DefaultHardErrorProcessing : 5
   +0x334 LastThreadExitStatus : 0n0
   +0x338 Peb              : (null) 
   +0x340 PrefetchTrace    : _EX_FAST_REF
   +0x348 ReadOperationCount : _LARGE_INTEGER 0xf
   +0x350 WriteOperationCount : _LARGE_INTEGER 0x0
   +0x358 OtherOperationCount : _LARGE_INTEGER 0x1ea
   +0x360 ReadTransferCount : _LARGE_INTEGER 0x2509758
   +0x368 WriteTransferCount : _LARGE_INTEGER 0x0
   +0x370 OtherTransferCount : _LARGE_INTEGER 0x130b
   +0x378 CommitChargeLimit : 0
   +0x380 CommitChargePeak : 0x50
   +0x388 AweInfo          : (null) 
   +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x398 Vm               : _MMSUPPORT
   +0x420 MmProcessLinks   : _LIST_ENTRY [ 0xfffffa80`0279b730 - 0xfffff800`0401c5e0 ]
   +0x430 HighestUserAddress : (null) 
   +0x438 ModifiedPageCount : 0x24ba
   +0x43c Flags2           : 0x2d800
   +0x43c JobNotReallyActive : 0y0
   +0x43c AccountingFolded : 0y0
   +0x43c NewProcessReported : 0y0
   +0x43c ExitProcessReported : 0y0
   +0x43c ReportCommitChanges : 0y0
   +0x43c LastReportMemory : 0y0
   +0x43c ReportPhysicalPageChanges : 0y0
   +0x43c HandleTableRundown : 0y0
   +0x43c NeedsHandleRundown : 0y0
   +0x43c RefTraceEnabled  : 0y0
   +0x43c NumaAware        : 0y0
   +0x43c ProtectedProcess : 0y1
   +0x43c DefaultPagePriority : 0y101
   +0x43c PrimaryTokenFrozen : 0y1
   +0x43c ProcessVerifierTarget : 0y0
   +0x43c StackRandomizationDisabled : 0y1
   +0x43c AffinityPermanent : 0y0
   +0x43c AffinityUpdateEnable : 0y0
   +0x43c PropagateNode    : 0y0
   +0x43c ExplicitAffinity : 0y0
   +0x440 Flags            : 0x14040800
   +0x440 CreateReported   : 0y0
   +0x440 NoDebugInherit   : 0y0
   +0x440 ProcessExiting   : 0y0
   +0x440 ProcessDelete    : 0y0
   +0x440 Wow64SplitPages  : 0y0
   +0x440 VmDeleted        : 0y0
   +0x440 OutswapEnabled   : 0y0
   +0x440 Outswapped       : 0y0
   +0x440 ForkFailed       : 0y0
   +0x440 Wow64VaSpace4Gb  : 0y0
   +0x440 AddressSpaceInitialized : 0y10
   +0x440 SetTimerResolution : 0y0
   +0x440 BreakOnTermination : 0y0
   +0x440 DeprioritizeViews : 0y0
   +0x440 WriteWatch       : 0y0
   +0x440 ProcessInSession : 0y0
   +0x440 OverrideAddressSpace : 0y0
   +0x440 HasAddressSpace  : 0y1
   +0x440 LaunchPrefetched : 0y0
   +0x440 InjectInpageErrors : 0y0
   +0x440 VmTopDown        : 0y0
   +0x440 ImageNotifyDone  : 0y0
   +0x440 PdeUpdateNeeded  : 0y0
   +0x440 VdmAllowed       : 0y0
   +0x440 CrossSessionCreate : 0y0
   +0x440 ProcessInserted  : 0y1
   +0x440 DefaultIoPriority : 0y010
   +0x440 ProcessSelfDelete : 0y0
   +0x440 SetTimerResolutionLink : 0y0
   +0x444 ExitStatus       : 0n259
   +0x448 VadRoot          : _MM_AVL_TABLE
   +0x488 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x4a8 TimerResolutionLink : _LIST_ENTRY [ 0x00000000`00000000 - 0x0 ]
   +0x4b8 RequestedTimerResolution : 0
   +0x4bc ActiveThreadsHighWatermark : 0x6f
   +0x4c0 SmallestTimerResolution : 0
   +0x4c8 TimerResolutionStackRecord : (null) 

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

/************************************************************************
*  Name : APEnumProcessInfoByIterateThirdLevelHandleTable
*  Param: TableCode
*  Param: EProcess
*  Param: pti
*  Param: ThreadCount
*  Ret  : VOID
*  遍历三级表
************************************************************************/
VOID
APEnumProcessInfoByIterateThirdLevelHandleTable(IN UINT_PTR TableCode,
	OUT PPROCESS_INFORMATION pi, IN UINT32 ProcessCount)
{
	do
	{
		APEnumProcessInfoByIterateSecondLevelHandleTable(TableCode, pi, ProcessCount);
		TableCode += sizeof(UINT_PTR);
	} while (*(PUINT_PTR)TableCode != 0 && MmIsAddressValid((PVOID)*(PUINT_PTR)TableCode));
}
NTSTATUS
APEnumProcessInfoByIteratePspCidTable(OUT PPROCESS_INFORMATION pi, IN UINT32 ProcessCount)
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	// 保存之前的模式,转成KernelMode
	PETHREAD EThread = PsGetCurrentThread();
	UINT8    PreviousMode = APChangeThreadMode(EThread, KernelMode);
	UINT_PTR PspCidTable = APGetPspCidTableAddress();
	APChangeThreadMode(EThread, PreviousMode);
	pi->NumberOfProcesses = 1;    // 先把0号成员预留出来,存放Idle
	// EnumHandleTable
	if (PspCidTable)
	{
		PHANDLE_TABLE	HandleTable = NULL;
		HandleTable = (PHANDLE_TABLE)(*(PUINT_PTR)PspCidTable);  	// HandleTable = fffff8a0`00004910
		if (HandleTable && MmIsAddressValid((PVOID)HandleTable))
		{
			UINT8			TableLevel = 0;		// 指示句柄表层数
			UINT_PTR		TableCode = 0;			// 地址存放句柄表首地址
			TableCode = HandleTable->TableCode & 0xFFFFFFFFFFFFFFFC;	// TableCode = 0xfffff8a0`00fc5000
			TableLevel = HandleTable->TableCode & 0x03;	                // TableLevel = 0x01
			if (TableCode && MmIsAddressValid((PVOID)TableCode))
			{
				switch (TableLevel)
				{
				case 0:
				{
					// 一层表
					APEnumProcessInfoByIterateFirstLevelHandleTable(TableCode, pi, ProcessCount);
					break;
				}
				case 1:
				{
					// 二层表
					APEnumProcessInfoByIterateSecondLevelHandleTable(TableCode, pi, ProcessCount);
					break;
				}
				case 2:
				{
					// 三层表
					APEnumProcessInfoByIterateThirdLevelHandleTable(TableCode, pi, ProcessCount);
					break;
				}
				default:
					break;
				}
			}
		}
	}
	if (pi->NumberOfProcesses > 1)
	{
		// 填充Idle的信息
		pi->ProcessEntry[0].ProcessId = 0;
		pi->ProcessEntry[0].EProcess = (UINT_PTR)APGetPsIdleProcess();   // 全局导出
		pi->ProcessEntry[0].ParentProcessId = 0;
		Status = STATUS_SUCCESS;
	}
	DbgPrint("EnumProcessInfo by iterate PspCidTable\r\n");
	return Status;
}

但是现在有个问题,如果在应用层打开文件,根据句柄,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: kd> u PsLookupProcessByProcessId l 40
nt!PsLookupProcessByProcessId:
fffff800`0416e1fc 48895c2408      mov     qword ptr [rsp+8],rbx
fffff800`0416e201 48896c2410      mov     qword ptr [rsp+10h],rbp
fffff800`0416e206 4889742418      mov     qword ptr [rsp+18h],rsi
fffff800`0416e20b 57              push    rdi
fffff800`0416e20c 4154            push    r12
fffff800`0416e20e 4155            push    r13
fffff800`0416e210 4883ec20        sub     rsp,20h
fffff800`0416e214 65488b3c2588010000 mov   rdi,qword ptr gs:[188h]
fffff800`0416e21d 4533e4          xor     r12d,r12d
fffff800`0416e220 488bea          mov     rbp,rdx
fffff800`0416e223 66ff8fc4010000  dec     word ptr [rdi+1C4h]
fffff800`0416e22a 498bdc          mov     rbx,r12
fffff800`0416e22d 488bd1          mov     rdx,rcx
fffff800`0416e230 488b0d9149edff  mov     rcx,qword ptr [nt!PspCidTable (fffff800`04042bc8)]
fffff800`0416e237 e834480200      call    nt!ExMapHandleToPointer (fffff800`04192a70)
fffff800`0416e23c 458d6c2401      lea     r13d,[r12+1]

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

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


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

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会占用他。所以就可以了。

HANDLE GetCsrPid()
{
  HANDLE Process, hObject;
  HANDLE CsrId = (HANDLE)0;
  OBJECT_ATTRIBUTES obj;
  CLIENT_ID cid;
  UCHAR Buff[0x100];
  POBJECT_NAME_INFORMATION ObjName = (PVOID)&Buff;
  PSYSTEM_HANDLE_INFORMATION_EX Handles;
  ULONG r;
 
  Handles = GetInfoTable(SystemHandleInformation);
 
  if (!Handles) return CsrId;
 
  for (r = 0; r < Handles->NumberOfHandles; r++)
  {
    if (Handles->Information[r].ObjectTypeNumber == 21) //Port object
    {
      InitializeObjectAttributes(&obj, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
 
      cid.UniqueProcess = (HANDLE)Handles->Information[r].ProcessId;
      cid.UniqueThread = 0;
 
      if (NT_SUCCESS(NtOpenProcess(&Process, PROCESS_DUP_HANDLE, &obj, &cid)))
      {
        if (NT_SUCCESS(ZwDuplicateObject(Process, (HANDLE)Handles->Information[r].Handle,NtCurrentProcess(), &hObject, 0, 0, DUPLICATE_SAME_ACCESS)))
        {
          if (NT_SUCCESS(ZwQueryObject(hObject, ObjectNameInformation, ObjName, 0x100, NULL)))
          {
            if (ObjName->Name.Buffer && !wcsncmp(L"\\Windows\\ApiPort", ObjName->Name.Buffer, 20))
            {
              CsrId = (HANDLE)Handles->Information[r].ProcessId;
            }
          }
 
          ZwClose(hObject);
        }
 
        ZwClose(Process);
      }
    }
  }
 
  ExFreePool(Handles);
  return CsrId;
}

因为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识别

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

nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x160 ProcessLock      : _EX_PUSH_LOCK
   +0x168 CreateTime       : _LARGE_INTEGER//创建时间
   +0x170 ExitTime         : _LARGE_INTEGER//已经消亡了,僵尸进程这里是个点
   +0x178 RundownProtect   : _EX_RUNDOWN_REF//进程保护,类似个锁,可以防止进程被结束掉,比如attach别人进程先加个锁,然后detach再释放
   +0x180 UniqueProcessId  : Ptr64 Void//pid
   +0x188 ActiveProcessLinks : _LIST_ENTRY
   +0x198 ProcessQuotaUsage : [2] Uint8B
   +0x1a8 ProcessQuotaPeak : [2] Uint8B
   +0x1b8 CommitCharge     : Uint8B
   +0x1c0 QuotaBlock       : Ptr64 _EPROCESS_QUOTA_BLOCK
   +0x1c8 CpuQuotaBlock    : Ptr64 _PS_CPU_QUOTA_BLOCK
   +0x1d0 PeakVirtualSize  : Uint8B
   +0x1d8 VirtualSize      : Uint8B
   +0x1e0 SessionProcessLinks : _LIST_ENTRY//进程检测相关
   +0x1f0 DebugPort        : Ptr64 Void
   +0x1f8 ExceptionPortData : Ptr64 Void
   +0x1f8 ExceptionPortValue : Uint8B
   +0x1f8 ExceptionPortState : Pos 0, 3 Bits
   +0x200 ObjectTable      : Ptr64 _HANDLE_TABLE//进程句柄链表
   +0x208 Token            : _EX_FAST_REF//令牌权限相关
   +0x210 WorkingSetPage   : Uint8B
   +0x218 AddressCreationLock : _EX_PUSH_LOCK
   +0x220 RotateInProgress : Ptr64 _ETHREAD
   +0x228 ForkInProgress   : Ptr64 _ETHREAD
   +0x230 HardwareTrigger  : Uint8B
   +0x238 PhysicalVadRoot  : Ptr64 _MM_AVL_TABLE//占用的物理内存
   +0x240 CloneRoot        : Ptr64 Void
   +0x248 NumberOfPrivatePages : Uint8B
   +0x250 NumberOfLockedPages : Uint8B
   +0x258 Win32Process     : Ptr64 Void
   +0x260 Job              : Ptr64 _EJOB
   +0x268 SectionObject    : Ptr64 Void//这个和下面可以获得进程对应的模块路径
   +0x270 SectionBaseAddress : Ptr64 Void
   +0x278 Cookie           : Uint4B
   +0x27c UmsScheduledThreads : Uint4B
   +0x280 WorkingSetWatch  : Ptr64 _PAGEFAULT_HISTORY
   +0x288 Win32WindowStation : Ptr64 Void
   +0x290 InheritedFromUniqueProcessId : Ptr64 Void
   +0x298 LdtInformation   : Ptr64 Void
   +0x2a0 Spare            : Ptr64 Void
   +0x2a8 ConsoleHostProcess : Uint8B
   +0x2b0 DeviceMap        : Ptr64 Void
   +0x2b8 EtwDataSource    : Ptr64 Void
   +0x2c0 FreeTebHint      : Ptr64 Void
   +0x2c8 FreeUmsTebHint   : Ptr64 Void
   +0x2d0 PageDirectoryPte : _HARDWARE_PTE
   +0x2d0 Filler           : Uint8B
   +0x2d8 Session          : Ptr64 Void
   +0x2e0 ImageFileName    : [15] UChar
   +0x2ef PriorityClass    : UChar
   +0x2f0 JobLinks         : _LIST_ENTRY
   +0x300 LockedPagesList  : Ptr64 Void
   +0x308 ThreadListHead   : _LIST_ENTRY
   +0x318 SecurityPort     : Ptr64 Void//32位指向一个peb64为
   +0x320 Wow64Process     : Ptr64 Void
   +0x328 ActiveThreads    : Uint4B
   +0x32c ImagePathHash    : Uint4B
   +0x330 DefaultHardErrorProcessing : Uint4B
   +0x334 LastThreadExitStatus : Int4B
   +0x338 Peb              : Ptr64 _PEB
   +0x340 PrefetchTrace    : _EX_FAST_REF
   +0x348 ReadOperationCount : _LARGE_INTEGER
   +0x350 WriteOperationCount : _LARGE_INTEGER
   +0x358 OtherOperationCount : _LARGE_INTEGER
   +0x360 ReadTransferCount : _LARGE_INTEGER
   +0x368 WriteTransferCount : _LARGE_INTEGER
   +0x370 OtherTransferCount : _LARGE_INTEGER
   +0x378 CommitChargeLimit : Uint8B
   +0x380 CommitChargePeak : Uint8B
   +0x388 AweInfo          : Ptr64 Void
   +0x390 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x398 Vm               : _MMSUPPORT
   +0x420 MmProcessLinks   : _LIST_ENTRY
   +0x430 HighestUserAddress : Ptr64 Void
   +0x438 ModifiedPageCount : Uint4B
   +0x43c Flags2           : Uint4B
   +0x43c JobNotReallyActive : Pos 0, 1 Bit
   +0x43c AccountingFolded : Pos 1, 1 Bit
   +0x43c NewProcessReported : Pos 2, 1 Bit
   +0x43c ExitProcessReported : Pos 3, 1 Bit
   +0x43c ReportCommitChanges : Pos 4, 1 Bit
   +0x43c LastReportMemory : Pos 5, 1 Bit
   +0x43c ReportPhysicalPageChanges : Pos 6, 1 Bit
   +0x43c HandleTableRundown : Pos 7, 1 Bit
   +0x43c NeedsHandleRundown : Pos 8, 1 Bit
   +0x43c RefTraceEnabled  : Pos 9, 1 Bit
   +0x43c NumaAware        : Pos 10, 1 Bit
   +0x43c ProtectedProcess : Pos 11, 1 Bit
   +0x43c DefaultPagePriority : Pos 12, 3 Bits
   +0x43c PrimaryTokenFrozen : Pos 15, 1 Bit
   +0x43c ProcessVerifierTarget : Pos 16, 1 Bit
   +0x43c StackRandomizationDisabled : Pos 17, 1 Bit
   +0x43c AffinityPermanent : Pos 18, 1 Bit
   +0x43c AffinityUpdateEnable : Pos 19, 1 Bit
   +0x43c PropagateNode    : Pos 20, 1 Bit
   +0x43c ExplicitAffinity : Pos 21, 1 Bit
   +0x440 Flags            : Uint4B
   +0x440 CreateReported   : Pos 0, 1 Bit
   +0x440 NoDebugInherit   : Pos 1, 1 Bit
   +0x440 ProcessExiting   : Pos 2, 1 Bit
   +0x440 ProcessDelete    : Pos 3, 1 Bit
   +0x440 Wow64SplitPages  : Pos 4, 1 Bit
   +0x440 VmDeleted        : Pos 5, 1 Bit
   +0x440 OutswapEnabled   : Pos 6, 1 Bit
   +0x440 Outswapped       : Pos 7, 1 Bit
   +0x440 ForkFailed       : Pos 8, 1 Bit
   +0x440 Wow64VaSpace4Gb  : Pos 9, 1 Bit
   +0x440 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x440 SetTimerResolution : Pos 12, 1 Bit
   +0x440 BreakOnTermination : Pos 13, 1 Bit
   +0x440 DeprioritizeViews : Pos 14, 1 Bit
   +0x440 WriteWatch       : Pos 15, 1 Bit
   +0x440 ProcessInSession : Pos 16, 1 Bit
   +0x440 OverrideAddressSpace : Pos 17, 1 Bit
   +0x440 HasAddressSpace  : Pos 18, 1 Bit
   +0x440 LaunchPrefetched : Pos 19, 1 Bit
   +0x440 InjectInpageErrors : Pos 20, 1 Bit
   +0x440 VmTopDown        : Pos 21, 1 Bit
   +0x440 ImageNotifyDone  : Pos 22, 1 Bit
   +0x440 PdeUpdateNeeded  : Pos 23, 1 Bit
   +0x440 VdmAllowed       : Pos 24, 1 Bit
   +0x440 CrossSessionCreate : Pos 25, 1 Bit
   +0x440 ProcessInserted  : Pos 26, 1 Bit
   +0x440 DefaultIoPriority : Pos 27, 3 Bits
   +0x440 ProcessSelfDelete : Pos 30, 1 Bit
   +0x440 SetTimerResolutionLink : Pos 31, 1 Bit
   +0x444 ExitStatus       : Int4B
   +0x448 VadRoot          : _MM_AVL_TABLE
   +0x488 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x4a8 TimerResolutionLink : _LIST_ENTRY
   +0x4b8 RequestedTimerResolution : Uint4B
   +0x4bc ActiveThreadsHighWatermark : Uint4B
   +0x4c0 SmallestTimerResolution : Uint4B
   +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: kd> dt _OBJECT_HEADER
nt!_OBJECT_HEADER
   +0x000 PointerCount     : Int8B
   +0x008 HandleCount      : Int8B
   +0x008 NextToFree       : Ptr64 Void
   +0x010 Lock             : _EX_PUSH_LOCK
   +0x018 TypeIndex        : UChar
   +0x019 TraceFlags       : UChar
   +0x01a InfoMask         : UChar
   +0x01b Flags            : UChar
   +0x020 ObjectCreateInfo : Ptr64 _OBJECT_CREATE_INFORMATION
   +0x020 QuotaBlockCharged : Ptr64 Void
   +0x028 SecurityDescriptor : Ptr64 Void
   +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: kd> dt _PEB
nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess  : Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 SpareBits        : Pos 5, 3 Bits
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA

里面的LDR

1: kd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
   +0x000 Length           : Uint4B
   +0x004 Initialized      : UChar
   +0x008 SsHandle         : Ptr64 Void
   +0x010 InLoadOrderModuleList : _LIST_ENTRY
   +0x020 InMemoryOrderModuleList : _LIST_ENTRY
   +0x030 InInitializationOrderModuleList : _LIST_ENTRY
   +0x040 EntryInProgress  : Ptr64 Void
   +0x048 ShutdownInProgress : UChar
   +0x050 ShutdownThreadId : Ptr64 Void

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

1: kd> dt _LDR_DATA_TABLE_ENTRY
nt!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x010 InMemoryOrderLinks : _LIST_ENTRY
   +0x020 InInitializationOrderLinks : _LIST_ENTRY
   +0x030 DllBase          : Ptr64 Void
   +0x038 EntryPoint       : Ptr64 Void
   +0x040 SizeOfImage      : Uint4B
   +0x048 FullDllName      : _UNICODE_STRING
   +0x058 BaseDllName      : _UNICODE_STRING
   +0x068 Flags            : Uint4B
   +0x06c LoadCount        : Uint2B
   +0x06e TlsIndex         : Uint2B
   +0x070 HashLinks        : _LIST_ENTRY
   +0x070 SectionPointer   : Ptr64 Void
   +0x078 CheckSum         : Uint4B
   +0x080 TimeDateStamp    : Uint4B
   +0x080 LoadedImports    : Ptr64 Void
   +0x088 EntryPointActivationContext : Ptr64 _ACTIVATION_CONTEXT
   +0x090 PatchInformation : Ptr64 Void
   +0x098 ForwarderLinks   : _LIST_ENTRY
   +0x0a8 ServiceTagLinks  : _LIST_ENTRY
   +0x0b8 StaticLinks      : _LIST_ENTRY
   +0x0c8 ContextInformation : Ptr64 Void
   +0x0d0 OriginalBase     : Uint8B
   +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: kd> !object \\
Object: fffff8a0000040d0  Type: (fffffa8001848240) Directory
    ObjectHeader: fffff8a0000040a0 (new version)
    HandleCount: 0  PointerCount: 42
    Directory Object: 00000000  Name: \

    Hash Address          Type          Name
    ---- -------          ----          ----
     01  fffff8a0000068f0 Directory     ObjectTypes
     05  fffff8a0003f92d0 SymbolicLink  SystemRoot
     06  fffff8a00013fc50 Directory     Sessions
     07  fffffa80036c8090 ALPC Port     MmcssApiPort
     08  fffff8a00000c260 Directory     ArcName
     09  fffff8a000071d30 Directory     NLS
     10  fffffa80036e2c00 ALPC Port     ThemeApiPort
         fffff8a0001321e0 Directory     Windows
         fffffa80038c4660 Event         LanmanServerAnnounceEvent
         fffff8a000008060 Directory     GLOBAL??
     11  fffff8a000139080 Directory     RPC Control
     13  fffffa8002f757a0 Event         EFSInitEvent
     14  fffffa8001d62480 Device        clfs
         fffff8a0002edde0 SymbolicLink  Dfs
     15  fffffa800279cb80 ALPC Port     SeRmCommandPort
         fffffa80026b9860 Event         CsrSbSyncEvent
     16  fffff8a000004370 SymbolicLink  DosDevices
     17  fffff8a005ebb2e0 Directory     KnownDlls32
     18  fffff8a00001aca0 Key           \REGISTRY
     19  fffff8a003261c90 Directory     BaseNamedObjects
     20  fffffa80018f0830 ALPC Port     PowerPort
     21  fffffa8003640bd0 ALPC Port     SmSsWinStationApiPort
         fffffa800276af20 Event         UniqueInteractiveSessionIdEvent
         fffff8a00006f390 Directory     UMDFCommunicationPorts
     22  fffff8a00396cbd0 Directory     KnownDlls
         fffffa80018f0140 ALPC Port     PowerMonitorPort
     23  fffff8a000006eb0 Directory     KernelObjects
         fffff8a00006f060 Directory     FileSystem
         fffffa8001d5c520 Device        Ntfs
     26  fffff8a000006060 Directory     Callback
         fffffa8003579090 ALPC Port     SeLsaCommandPort
     28  fffff8a00000b060 Directory     Security
     29  fffffa800371d6a0 ALPC Port     UxSmsApiPort
     30  fffff8a000010920 Directory     Device
     32  fffffa80035a7260 Event         DSYSDBG.Debug.Trace.Memory.218
     34  fffffa800279f610 ALPC Port     SmApiPort
         fffff8a000f87080 Section       LsaPerformance
         fffffa80027a4510 Event         UniqueSessionIdEvent
     36  fffff8a000071b80 Directory     Driver
         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

int PTESize;
UINT_PTR PAGE_SIZE_LARGE;
UINT_PTR MAX_PDE_POS;
UINT_PTR MAX_PTE_POS;
 
struct PTEStruct
{
	unsigned P         :  1; // present (1 = present)是否存在
	unsigned RW        :  1; // read/write
	unsigned US        :  1; // user/supervisor
	unsigned PWT       :  1; // page-level write-through
	unsigned PCD       :  1; // page-level cache disabled
	unsigned A         :  1; // accessed
	unsigned Reserved  :  1; // dirty
	unsigned PS        :  1; // page size (0 = 4-KB page)
	unsigned G         :  1; // global page
	unsigned A1		   :  1; // available 1 aka copy-on-write
	unsigned A2		   :  1; // available 2/ is 1 when paged to disk
	unsigned A3		   :  1; // available 3
	unsigned PFN       : 20; // page-frame number
};
 
void InitMemSafe()
{
#ifndef AMD64
	ULONG cr4reg;
    //determine if PAE is used
	cr4reg=(ULONG)__readcr4();
	if ((cr4reg & 0x20)==0x20)
	{
		PTESize=8; //pae
		PAGE_SIZE_LARGE=0x200000;
		MAX_PDE_POS=0xC0604000;
		MAX_PTE_POS=0xC07FFFF8;
	}
	else
	{
		PTESize=4;
		PAGE_SIZE_LARGE=0x400000;
		MAX_PDE_POS=0xC0301000;
		MAX_PTE_POS=0xC03FFFFC;
	}
#else
	PTESize=8; //pae
	PAGE_SIZE_LARGE=0x200000;
	MAX_PTE_POS=0xFFFFF6FFFFFFFFF8ULL;
	MAX_PDE_POS=0xFFFFF6FB7FFFFFF8ULL;
#endif
}
 
BOOLEAN IsAddressSafe(UINT_PTR StartAddress)
{
	#ifdef AMD64
	//cannonical check. Bits 48 to 63 must match bit 47
	UINT_PTR toppart=(StartAddress >> 47);
	if (toppart & 1)
	{
		//toppart must be 0x1ffff
		if (toppart != 0x1ffff)
			return FALSE;
	}
	else
	{
		//toppart must be 0
		if (toppart != 0)
			return FALSE;
 
	}
	#endif
	//PDT+PTE judge
	{
	#ifdef AMD64
		UINT_PTR kernelbase=0x7fffffffffffffffULL;
		if (StartAddress<kernelbase)
		{
			return TRUE;
		}
		else
		{
			PHYSICAL_ADDRESS physical;
			physical.QuadPart=0;
			physical=MmGetPhysicalAddress((PVOID)StartAddress);
			return (physical.QuadPart!=0);
		}
		return TRUE; //for now untill I ave figure out the win 4 paging scheme
	#else
		ULONG kernelbase=0x7ffe0000;
		UINT_PTR PTE,PDE;
		struct PTEStruct *x;
		if (StartAddress<kernelbase)
		{
			return TRUE;
		}
		PTE=(UINT_PTR)StartAddress;
		PTE=PTE/0x1000*PTESize+0xc0000000;
		//now check if the address in PTE is valid by checking the page table directory at 0xc0300000 (same location as CR3 btw)
		PDE=PTE/0x1000*PTESize+0xc0000000; //same formula
		x=(struct PTEStruct *)PDE;
		if ((x->P==0) && (x->A2==0))
		{
			//Not present or paged, and since paging in this area isn't such a smart thing to do just skip it
			//perhaps this is only for the 4 mb pages, but those should never be paged out, so it should be 1
			//bah, I've got no idea what this is used for
			return FALSE;
		}
		if (x->PS==1)
		{
			//This is a 4 MB page (no pte list)
			//so, (startaddress/0x400000*0x400000) till ((startaddress/0x400000*0x400000)+(0x400000-1) ) ) is specified by this page
		}
		else //if it's not a 4 MB page then check the PTE
		{
			//still here so the page table directory agreed that it is a usable page table entry
			x=(PVOID)PTE;
			if ((x->P==0) && (x->A2==0))
				return FALSE; //see for explenation the part of the PDE
		}
		return TRUE;
	#endif
	}
}

禁止功能

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


{nPid,NtOpenProcess() NtTerminateProcess}

禁止创建进程,

FltRegisterFilter

自我保护

通过hook那些修改的函数。

Win7 X64 ShadowSSDT hook

;知道shadow ssdt serviceTable[] ULONG

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

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值