NTFS文件解析系统的简单分析.docx
《NTFS文件解析系统的简单分析.docx》由会员分享,可在线阅读,更多相关《NTFS文件解析系统的简单分析.docx(14页珍藏版)》请在冰豆网上搜索。
NTFS文件解析系统的简单分析
NTFS文件解析系统的简单分析
Author:
MTricksterQQ:
56329475E-Mail:
MTrickster@
正如我们所知道的,目前主流anti-rootkit检测隐藏文件主要有两种方法,一种是文件系统层的检测(像IceSword自己构造irp包发到文件系统驱动),另一种就是磁盘级别的低级检测,直接对磁盘的数据进行分析,比如SnipeSword、filereg和unhooker.
最近对第二种方法的一部分(及NTFS文件系统的解析)做了一些学习,于是就写点学习的东西与大家分享,其中有很多错误和不足希望大牛们指出。
(1)准备工作――获取分区的一些基本参数
我们可以用CreateFile打开某个盘,读取偏移为0的扇区的数据。
具体的数据结构为:
typedefstructtag_NTFSBPB
{
BYTEbJmp[3];//跳转指令
BYTEbNTFlags[4];//文件系统NTFS的为"NTFS"
BYTEbReserve1[4];//一般为四个空格
WORDwBytePerSector;//每扇区字节数
BYTEbSectorPerCluster//;每簇扇区数
WORDwReserveSectors;//保留扇区数
BYTEbFatNum;//总是0
WORDwRootDirNum;//总是0
WORDwSectorOfParti;//总是0
BYTEbMedium;//
WORDwSectorPerFat;//总是0
WORDwSectorPerTrack;//
WORDwHeadNum;//
DWORDdwHideSector;//
DWORDdwSectoOfParti;//总是0
BYTEbDeviceFlag;//
BYTEbReserve2;//
WORDwReserve3;//
ULONGLONGullSectorsOfParti;//扇区总数
ULONGLONGullMFTAddr;//$MFT的起始逻辑簇号
ULONGLONGullMFTMirrAddr;//$MFT的起始逻辑簇号
BYTEbClusterPerFile;//
BYTEbReserve4[3];//
DWORDdwClusterPerINDX;//
BYTEbSerialID[8];//
}NTFSBPB,*LPNTFSBPB;
其中最重要的应该就是通过ullMFTAddr获得$MFT的起始逻辑簇号进而找到根
图
(1)
目录的位置在进行分析。
比如图
(1)是通过winhex看到的我的c盘的BPB,其中ullMFTAddr为0x0c0000,根据bSectorPerCluster和wBytePerSector确定$MFT在偏移值为0x0c0000000处,如图
(2)
图
(2)
这是一个主控文件表,其数据结构为:
typedefstructtag_MFTHEAD{
BYTEbHeadID[4];//MFT标志,一定为FILE
USHORTusFixupOffset;//更新序列号的偏移
USHORTusFixupNum;//更新序列号的大小
BYTEbReserve1[8];//
WORDwUnknownSeqNum;//
USHORTusLinkNum;//
USHORTusAttrOffset;//第一个属性的偏移地址
WORDwResident;//文件属性标志
ULONGulMFTSize;//文件记录的实际长度
ULONGulMFTAllocSize;//记录分配的大小
ULONGLONGullMainMFT;//基本文件记录的文家索引号
WORDwNextFreeID;//
WORDwFixup[0x10];//
}MFTHEAD,*LPMFTHEAD;
(2)找到根目录位置
我们依次向下找到元数据$Root及根目录的MFT头,一般再其后的0x1400处
图(3)
(2)分析根目录的索引根和索引分配
这应该是解析的最关键的部分了,其中几个重要的数据结构如下。
typedefstructtag_RESIDATTR{
ULONGulDataSize;
USHORTusRDataOffset;
WORDwUnknownAttrIndexID;//
}RESIDATTR,*LPRESIDATTR;
typedefstructtag_NONRESIDATTR{
ULONGLONGullVCNStart;
ULONGLONGullVCNEnd;
USHORTusNrDataOffset;
WORDwComprEngine;//
DWORDdeReserve2;
ULONGLONGullAllocSize;
ULONGLONGullDataSize;
ULONGLONGullInitSize;
ULONGLONGullComprSize;//
}NONRESIDATTR,*LPNONRESIDATTR;
typedefstructtag_MFTATTR{
DWORDdwAttrType;//属性类型
USHORTusAttrSize;//属性大小(包含属性头)
WORDwReserve1;
BYTEbISResident;
BYTEbLenName;//属性名长度
USHORTusDataOffset;//该属性开始的偏移
WORDwISCompr;//
WORDwAttrID;//属性ID
unionunAttrib
{
RESIDATTRResidAttr;
NONRESIDATTRNonResidAttr;
}unAttrib;
}MFTATTR,*LPMFTATTR;
(a)获取索引根和索引分配
根据tag_MFTHEAD中的usAttrOffset找到第一个属性地址,图(3)中usAttrOffset为0x38所以第一个属性在0x0c0001438处,这里第一个属性为$STANDARD_INFORMATION,不是我们要找的,加上该属性大小usAttrSize到第二个属性处,依次找到索引根和索引分配属性。
while($ATTR_LIST!
=pAttr->dwAttrType&&0!
=pAttr->dwAttrType&&-1L!
=pAttr->dwAttrType)
{
pAttr=LPMFTATTR(LPBYTE(pAttr)+pAttr->usAttrSize);
}
图(4)
图(5)
(b)分析索引根和索引分配
完成了a部后我们就能开始具体对他们进行分析了。
首先是对根索引的分析,该属性是实现NTFS的B+树索引的根节点。
其中主要的数据结构是索引头、索引项。
typedefstructtag_INDXHEAD
{
DWORDdw1IndxOffset;//第一个索引项的偏移
DWORDdwIndxSize;//索引项的总大小
DWORDdwInxAlcSize;//索引项的分配
BYTEbFlags;
BYTEbPad[3];
}INDXHEAD,*LPINDXHEAD;
typedefstructtag_INDXENTRY
{
DWORDdwMFTIndx;//文件的MFT参考号
DWORDdwReserve1;
WORDwEntrySize;//索引项的大小
WORDwReserve2;
BYTEbISSubNode;
BYTEbReserve3[3];
DWORDdwAppear;
DWORDdwReserve4;
ULONGLONGullFileTime[4];
ULONGLONGullDataAlcSize;//分配文件的大小
ULONGLONGullDataSize;//真实的文件大小
ULONGLONGullReserve5;
BYTEbNameLen;//文件名的长度
BYTEbNameType;
WCHARwzFileName[MAX_PATH];//文件名
}INDXENTRY,*LPINDXENTRY;
typedefstructtag_INDXATTR
{
DWORDdwMFTIndx;
WORDwReserve1;
WORDwReserve2;
WORDwcbSize;
WORDwNameAttrLen;
WORDwISSubNode;
BYTEbReserve3[2];
DWORDdwParentMFTIndx;
DWORDdwReserve4;
ULONGLONGullCreateTime;
ULONGLONGullLastModTime;
ULONGLONGullModRcdTime;
ULONGLONGullLastAccTime;
ULONGLONGullAllocSize;
ULONGLONGullFileSize;
ULONGLONGullFileFlags;
BYTEbFileNameLen;
BYTEbFileNSpace;
WCHARwzFileName[MAX_PATH];
}INDXATTR,*LPINDXATTR;
通过这我们就可以对索引根中的索引进行历遍查询,找到根目录下的一部分文件,但我发现在这存储的文件索引一般不多,只有一些小目录存放在这。
还有不少一部分要通过索引分配分析出来。
为了分析索引分配我们就必须通过索引分配找到文件索引的具体位置,主要就是根据索引分配最后的运行列表去找。
具体的几个函数我摘自ReactOS中的
staticULONG
RunLength(PUCHARrun)
{
return(*run&0x0f)+((*run>>4)&0x0f)+1;
}
staticLONGLONG
RunLCN(PUCHARrun)
{
UCHARn1=*run&0x0f;
UCHARn2=(*run>>4)&0x0f;
LONGLONGlcn=(n2==0)?
0:
(CHAR)(run[n1+n2]);
LONGi=0;
for(i=n1+n2-1;i>n1;i--)
lcn=(lcn<<8)+run[i];
returnlcn;
}
staticULONGLONG
RunCount(PUCHARrun)
{
UCHARn=*run&0xf;
ULONGLONGcount=0;
ULONGi=0;
for(i=n;i>0;i--)
count=(count<<8)+run[i];
returncount;
}
BOOLEAN
FindRun(PNONRESIDENT_ATTRIBUTENresAttr,
ULONGLONGvcn,
PULONGLONGlcn,
PULONGLONGcount)
{
PUCHARrun;
ULONGLONGbase=NresAttr->StartVcn;
if(vcnStartVcn||vcn>NresAttr->LastVcn)
returnFALSE;
*lcn=0;
for(run=(PUCHAR)((ULONG)NresAttr+NresAttr->RunArrayOffset);
*run!
=0;run+=RunLength(run))
{
*lcn+=RunLCN(run);
*count=RunCount(run);
if(base<=vcn&&vcn
{
*lcn=(RunLCN(run)==0)?
0:
*lcn+vcn-base;
*count-=(ULONG)(vcn-base);
returnTRUE;
}
else
{
base+=*count;
}
}
returnFALSE;
}
具体的我们可以看winhex中的图(5)中0x0c000166A处,对应的索引文件就如图(6).
图(5)
图(6)
接下来就是对索引项的解析
其中具体的数据结构已经在上面列出这里就就将给出简单实现的函数为
VoidListDir(LPBYTEpDir,UINTuAlcSize)
{
LPINDXpIndx;
LPINDXATTRpIndxAttr;
HTREEITEMhrChild;
DWORDdwRes;
BYTEpRuns[MAX_RUNS];
pIndx=(LPINDX)pDir;
pIndxAttr=LPINDXATTR(pDir+(0x18+pIndx->wHeadSize));
if('I'!
=pIndx->bDirID[0]||'N'!
=pIndx->bDirID[1]||'D'!
=pIndx->bDirID[2]||'X'!
=pIndx->bDirID[3])
{
returnG_FAILED;
}
while(TRUE)
{
if((ULONG(pIndxAttr)-ULONG(pIndx))>=pIndx->dwUseSize)
{
if(0==uAlcSize-(pIndx->dwAllocSize+0x18))
break;
else
{
uAlcSize-=(pIndx->dwAllocSize+0x18);
pIndx=(LPINDX)((LPSTR)pIndx+(pIndx->dwAllocSiz+0x18));
if('I'!
=pIndx->bDirID[0]||
'N'!
=pIndx->bDirID[1]||
'D'!
=pIndx->bDirID[2]||
'X'!
=pIndx->bDirID[3])
{
return;
}
pIndxAttr=LPINDXATTR(LPSTR(pIndx)+(0x18+pIndx->wHeadSize));
}
}
if(12>pIndxAttr->dwMFTIndx)
{
pIndxAttr=LPINDXATTR(LPSTR(pIndxAttr)+pIndxAttr->wcbSize);
}
else
{
if(FALSE==(UNICODE_NAME&pIndxAttr->bFileNSpace))
{
pIndxAttr=LPINDXATTR(LPSTR(pIndxAttr)+pIndxAttr->wcbSize);
continue;
}
intiChar=WideCharToMultiByte(CP_ACP,0,pIndxAttr->wzFileName,pIndxAttr->bFileNameLen,
m_szName,MAX_PATH,NULL,NULL);
m_szName[iChar]='\0';
pIndxAttr=LPINDXATTR(LPSTR(pIndxAttr)+pIndxAttr->wcbSize);
m_1.AddString(m_szName);
}
}
Return;
}
具体的在winhex看到的就是图(7)的config.sys和图(8)的DocumentsandSettings
图(7)
图(8)
(c)当然接下来的就是根据得到的索引项列表得到文件的具体一些信息,比如文件的MFT文件参考号、文件大小、名字、属性(是否是目录)。
如果是目录根据MFT文件参考号定位到目录文件的MFT地址,在根据上面的方法继续查找。
(当然这是我用的方法,可能还有什么地方没有分析出来)