//Time: 2009,5,31
//Author : Sysnap
//Link: http://hi.baidu.com/sysnap
前言:
Yas Kit的进程检测因为对对象的判断太过严格,搜索对象办法也不是很好,导致检测的效果不好,而驱动检测方面也不大理想, 使用了其他ARK,发现也有相干的问题,于是找点时间调整原来的搜索办法,过程记录下来与各位分享,也希望各位能提出问题,解决之[Sysnap] 1 一个对象生成时,一般都调用了ObCreateObject,每个对象都有不同的数据结构,在内存用一段空间表示,所谓的内存搜索对象,就是在内存里找出这些数据结构出来.当然你应该猜到ObCreateObject会调用ExAllocatePoolWithTag,这个函数的原形是
PVOID
ExAllocatePoolWithTag(
IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag
);
下面我们讲IN ULONG Tag
2 效率
我们不能太暴力,尽量的获取相关信息来提高搜索的效率,其中Tag可以很好的利用. 对象管理器在分配一个对象时,根据不同的对象类型,ExAllocatePoolWithTag Tag也会不同,这给我们一个很好的搜索办法.就是在内存里找这些Tag,从而找到对象.
3搜索范围
一般对象数据结构的分配是在非分页内存的,系统有俩个非分页区,一般是在第一个分配,
这个非分页区的范围可以用俩个系统变量来标识.
MmNonPagedPoolStart和MmSizeOfPagedPoolInBytes,这俩个变量都是未导出,怎样定位呢?可以利用KPCR这个结构,
__asm
{
mov eax, fs:[0x1C] // SelfPCR 一般是0XFFDFF000
mov eax, [eax+0x34] // KdVersionBlock
mov KdVersionBlock, eax
}
KdVersionBlock 就可以获取相关的变量了..
4还需要什么
先看
lkd> dd IoDriverObjectType
80561d60 85ea9040 85e60100 85ea9e70 85e602d0
80561d70 85e604a0 0000000a 00000000 00000000
lkd> dt _object_type 85ea9040
nt!_OBJECT_TYPE
+0x000 Mutex : _ERESOURCE
+0x038 TypeList : _LIST_ENTRY [ 0x85e5fc68 - 0x826c7f10 ]
+0x040 Name : _UNICODE_STRING ""
+0x048 DefaultObject : 0x80569c60
+0x04c Index : 0x1a
+0x050 TotalNumberOfObjects : 0x81
+0x054 TotalNumberOfHandles : 0
+0x058 HighWaterNumberOfObjects : 0x81
+0x05c HighWaterNumberOfHandles : 1
+0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0ac Key : 0x76697244
+0x0b0 ObjectLocks : [4] _ERESOURCE
我们还需要 +0x0ac Key : 0x76697244 和 TotalNumberOfObjects
其中 Key 用于搜索用, TotalNumberOfObjects用于对比用,比如我们内存搜索驱动对象一共搜索到120个,,但TotalNumberOfObjects的值是110,,说明其中有10已经无效了
而 Key 这个跟Tag有关, 比如 驱动类型的对象 Key : 0x76697244
那么 驱动类型的 Tag是 0x76697244 | 0x80000000
其实就是'Driv' 你可以用 !poolfind Driv 命令来验证,,比如在我电脑输出
lkd> !poolfind Driv
unable to get PoolTrackTable - pool tagging is likely disabled or you have the wrong symbols
unable to get large pool allocation table - either wrong symbols or pool tagging is disabled
Searching NonPaged pool (81da1000 : 85ec6000) for Tag: Driv
83f31000 size: 108 previous size: 0 (Allocated) Driv (Protected)
840d2380 size: 328 previous size: 20 (Free) Dri.
8421c690 size: 108 previous size: 78 (Allocated) Driv (Protected)
84261cf0 size: 108 previous size: 40 (Allocated) Driv (Protected)
843bcd80 size: 108 previous size: 68 (Allocated) Driv (Protected)
843cb000 size: 108 previous size: 0 (Allocated) Driv (Protected)
84421320 size: 108 previous size: 8 (Allocated) Driv (Protected)
关于 84421320 , 843bcd80等是什么意义这个我不知道,但我发现这个地址在加上某个值时就是对象本身的地址....这个值怎么确定呢?比如对于驱动的,我们可以这样获取
下面其实就是逐渐验证方法,因为我不知道84421320代表的数据结构或者意义什么,也只能这么做了.
DWORD GetDriverOfNum()
{
DWORD dwI = 0;
OBJECT_TYPEINFO ObTypeInfo;
DWORD x = 0;
if(ObGetObjectTypeInfo(OBTYPE_DRIVER, &ObTypeInfo) == FALSE)
{
return 0;
}
for(dwI = gNonPagePoolInfo.dwStart; dwI < gNonPagePoolInfo.dwEnd ; dwI +=4)
{
if (IsAddressSafe((PVOID)dwI) == TRUE)
{
if(*(DWORD*)dwI == ObTypeInfo.dwKey)
{
PDRIVER_OBJECT pTmpDriObject = NULL;
for(x=0; x<0x150; x+=0x10)
{
pTmpDriObject = (PDRIVER_OBJECT)((dwI-4)+x);
if(pTmpDriObject->Type == 4)
{
DbgPrint("--hi find x == 0x%x",x);
return x;
break;
}
}
}
}
}
return 0;
}
5开始搜索
这里以搜索驱动为例子
其实也比较简单,这里我们从几个地方获取名字.
pTmpDriObject->DriverExtension->ServiceKeyName.Buffer,
pDriverSection->BaseDllName.Buffer,
pTmpDriObject->DriverName.Buffer,
从检测的效果上看,很不错...
VOID ScanDriverObject()
{
DWORD dwI = 0;
DWORD dwCount = 1;
DWORD dwDriMagic = 0;
OBJECT_TYPEINFO ObTypeInfo;
ANSI_STRING asString;
NTSTATUS ntStatus; if(ObGetObjectTypeInfo(OBTYPE_DRIVER, &ObTypeInfo))
{
DbgPrint("--%x %d",ObTypeInfo.dwKey,ObTypeInfo.dwTotalNumberOfObjects);
}
dwDriMagic = GetDriverOfNum();
for(dwI = gNonPagePoolInfo.dwStart; dwI < gNonPagePoolInfo.dwEnd ; dwI +=4)
{
if (IsAddressSafe((PVOID)dwI) == TRUE)
{
if(*(DWORD*)dwI == ObTypeInfo.dwKey)
{
PDRIVER_OBJECT pTmpDriObject = NULL;
pTmpDriObject =(PDRIVER_OBJECT)((dwI-4)+dwDriMagic);
if(pTmpDriObject->Type == 4)
{
PLDR_DATA_TABLE_ENTRY pDriverSection = (PLDR_DATA_TABLE_ENTRY)pTmpDriObject->DriverSection;
//if (IsAddressSafe((PVOID)pDriverSection->FullDllName.Buffer) == TRUE)
if(pDriverSection != 0)
{
ntStatus = RtlUnicodeStringToAnsiString(&asString,&pTmpDriObject->DriverName,TRUE);
if(NT_SUCCESS(ntStatus))
{
if(asString.Buffer[0] != '\\')
DbgPrint("%d %ws %ws %ws 0x%x 0x%x 0x%x %ws",dwCount,
pTmpDriObject->DriverExtension->ServiceKeyName.Buffer,
pDriverSection->BaseDllName.Buffer,
pTmpDriObject->DriverName.Buffer,
pTmpDriObject->DriverStart,
pTmpDriObject->DriverSize,
pTmpDriObject,
pDriverSection->FullDllName.Buffer);
}
//判断也许不用了..没必要...
//if(pTmpDriObject->DriverExtension->DriverObject == pTmpDriObject)
}
//DbgPrint("%d %x",dwCount, ((dwI-4)+dwDriMagic));
dwCount++;
}
}
}
}
} 6对象的有效性,这个不同的对象判断办法不同,也比较难弄全.比如进程我们可以判断EXITTME,但总不太完美
下面是一个搜索进程的例子,加了简单的对象有效判断.
/////////////////////////////////////////////////////////////////////////////////////
/////
////////////////////////////////////////////////////////////////////////////////////
DWORD GetProcessOfNum()
{
DWORD dwI = 0;
OBJECT_TYPEINFO ObTypeInfo;
DWORD x = 0;
if(ObGetObjectTypeInfo(OBTYPE_PROCESS, &ObTypeInfo) == FALSE)
{
return -1;
}
for(dwI = gNonPagePoolInfo.dwStart; dwI < gNonPagePoolInfo.dwEnd ; dwI +=4)
{
if (IsAddressSafe((PVOID)dwI) == TRUE)
{
if(*(DWORD*)dwI == ObTypeInfo.dwKey)
{
PVOID pTmpObject = NULL;
for(x=0; x<0x150; x+=0x10)
{
pTmpObject = (PVOID)((dwI-4)+x);
if(pTmpObject == (PVOID)PsGetCurrentProcess())
{
DbgPrint("--hi find x == 0x%x",x);
return x;
break;
}
}
}
}
}
return -1;
} DWORD ExitTimeOffset()
{
DWORD dw_i=0;
CHAR* cFuncAddress =0;
UNICODE_STRING uniFuncName; RtlInitUnicodeString(&uniFuncName,L"PsGetProcessExitTime");
cFuncAddress = (CHAR*)MmGetSystemRoutineAddress(&uniFuncName);
//DbgPrint("-xx -%x",cFuncAddress);
//if(dwFuncAddress ! = 0)
{
for(dw_i =0; dw_i < 16; dw_i++)
{
//DbgPrint("-xx -%x %x",cFuncAddress[dw_i]&0xff,cFuncAddress[dw_i+1]&0xff);
if( (cFuncAddress[dw_i]&0xff)==0x8b && (cFuncAddress[dw_i+1]&0xff)==0x41)
{
//DbgPrint("eeetttttttttttttttttt--");
return (DWORD)cFuncAddress[dw_i+2];
}
}
}
return 0;
/* /// XP SP3
lkd> u PsGetProcessExitTime
nt!PsGetProcessExitTime:
805e2dab 64a124010000 mov eax,dword ptr fs:[00000124h]
805e2db1 8b4844 mov ecx,dword ptr [eax+44h]
805e2db4 8b4178 mov eax,dword ptr [ecx+78h]
805e2db7 8b517c mov edx,dword ptr [ecx+7Ch]
805e2dba c3 ret
*/
} BOOLEAN MmScanProcessObject()
{
OBJECT_TYPEINFO ObTypeInfo;
DWORD dwPsMagic = 0;
DWORD dwI = 0;
DWORD dwCount =1;
DWORD dwExitTimeOffset = 0;
NTSTATUS ntStatus;
HANDLE hProcess;
dwPsMagic = GetProcessOfNum();
if((ObGetObjectTypeInfo(OBTYPE_PROCESS, &ObTypeInfo)==FALSE) || (dwPsMagic == -1))
{
return FALSE;
}
dwExitTimeOffset = ExitTimeOffset();
DbgPrint("TRUE OBJECT COUNT %d",ObTypeInfo.dwTotalNumberOfObjects);
for(dwI = gNonPagePoolInfo.dwStart; dwI < gNonPagePoolInfo.dwEnd ; dwI +=4)
{
if (IsAddressSafe((PVOID)dwI) == TRUE)
{
if(*(DWORD*)dwI == ObTypeInfo.dwKey)
{
PVOID pTmpObject = NULL;
pTmpObject =(PVOID)((dwI-4)+dwPsMagic);
if(*(UCHAR*)pTmpObject == 0x03) //xp sp3???
{
PLARGE_INTEGER ExitTime;
ExitTime = (PLARGE_INTEGER)((DWORD)pTmpObject + dwExitTimeOffset);
if(ExitTime->QuadPart == 0) //已经结束的进程的ExitTime为非零
DbgPrint("%08x %8d%s ",pTmpObject, PsGetProcessId(pTmpObject),
PsGetProcessImageFileName(pTmpObject));
//DbgPrint("%d EPROCESS 0x%x",dwCount,pTmpObject);
dwCount++;
}
}
}
}
return TRUE;
}
具体的代码见附件.
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: