首页
社区
课程
招聘
[求助]关于ntfs目录文件的解析有点问题
发表于: 2013-5-7 19:33 7106

[求助]关于ntfs目录文件的解析有点问题

2013-5-7 19:33
7106
想做一个读写硬盘删除指定文件。然后A了sudami前辈的那个ScsiReadWrite..看了一点他那个修改ntfs-3g做成的Ntfs解析。无奈水平太差在ntfs_inode_lookup_by_name()这个函数的细节上看不太懂,只能看懂个大概(水平太差b+树看了好多资料也没能搞懂。只知道是个各种规则的树结构)。ntfs目录中寻找指定名称的文件这块的代码。我现在自己实现了一个类似与目录枚举的搜索。在IndexRoot和IndexAllocate中包含的所有DataRun指向的IndexBlock中每个簇中暴利搜索我想要找到的文件名。现在能够通过给定路径找到指定文件的mft记录号。我觉得这么做有点不妥。
想做一个更稳妥,更科学的一边解析一边查找的方法。
所以想问问这些IndexBlock都是怎么连接在一起的。怎么从上一个得知下一个的位置
我先说一下我现在错误的做法。寻求大神们的指正。
比如我要解析Windows目录。已经取得Windows目录的MFT 记录号是 28然后从MFT中获取到
Windows的IndexRoot 中的IndexEntry为
00 00 00 00 00 00 00 00 18 00 00 00 03 00 00 00
06 00 00 00 00 00 00 00
即IndexRoot中没有实际的值,只是存放了子节点的lcn lcn = 6.
IndexAllocate 取到 DataRun 是
31 07 66 f9 0d 11 01 d9 00 40 61 e1 ec 6b df f3
将DataRun解析一下是
31 07 66 f9 0d
就是vcn = 915814
       size = 7;
11 01 d9
lcn = -39
size = 1
然后就以00结束了
至于00后面的。。我现在认为是以前有用的数据然后在目录增加删除过程中变化了的冗余数据。现在已经是无效的了。

取得了以上数据后我的解析如下过程:
读取第一个DataRun的vcn中的数据
遍历其中的IndexEntry直到IndexEntryEND(即标志了IndexEntryENd的IndexENtry);
然后如果这个IndexEntryEnd还指示出了有节点就把lcn作为相对于此IndexBlock VCN的深入递归遍历(即子节点的VCN = 第一个DATARUN的VCN+EntryEND中存储的LCN)。
然后再解析后面的DataRun做法同第一个是类似的只不过后面的IndexBlock的 VCN = 第一个的VCN + 后面DATaRun中的LCN
最后在DataRun都遍历完成以后。
遍历IndexRoot中EntryEND中存放的子节点。
同样是VCN = 第一个DATARUn VCN + ENTRYEND中存放的lcn.

然后我发现。。我遇到的问题是。我这样做。对于一个比较大的目录来说。只能找到目录下面的一部分文件和子文件夹。并不能全部找到。

我主要想问的就是,我的做法有什么错误。怎样的思路才能达到我想做到的遍历整个目录查找子文件或文件夹的目的。另外,小弟知道写的很乱。因为这个东西小弟觉得实在是理解的不好。如果您觉得您兴趣帮我。但是需要知道一些更详细的在下面回复就好。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (12)
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
下面放上现在我称之为暴利搜索的代码。。求保养。求看有多挫。
int ParseDataRun(BYTE * pData, int * nOffset, DWORD * dwLength)
{
        int offsetBits, lengthBits;
        if(pData[0] == 0)
        {
                *nOffset = 0;
                *dwLength = 0;
                return 0;
        }
        offsetBits = (pData[0] >> 4) * 8;
        lengthBits = (pData[0] & 0x0f) * 8;
        if(offsetBits > 32 || lengthBits > 32)
        {
                dprintf("can't handle DataRun size");
                *dwLength = 0;
                *nOffset = 0;
                return -1;
        }
        //计算length
        *dwLength = *(PULONG)(pData + 1) & ~(LENMASK << offsetBits);
        //计算offset因为有正负所以稍复杂在不足DWORD的时候要用远数字 减去 那个100000..原数字位个0取得符号

        //判断最高位即标志位是否为1                                                                                                                        
        *nOffset = (*(PULONG)(pData + 1 + lengthBits / 8) & 0x1 << (offsetBits - 1)) ?
                //为1则为负数转换成32位的此负数表示
                (*(PULONG)(pData + 1 + lengthBits / 8) & ~(LENMASK << offsetBits)) - (OFFSETMASK << lengthBits) :  
                        //否则为正数无需转换               
                        (*(PULONG)(pData + 1 + lengthBits / 8) & ~(LENMASK << offsetBits));
        return (offsetBits + lengthBits) / 8 + 1;
}

DWORD FindSplice(ULONG Sector, wchar_t * splice, DWORD IndexRootRecordSector)
//sector开始扇区
//splice需要查找的名称字符串
//IndexRootRecordSector是第一个DataRun指向的VCN
{
        PCHAR pBuffer;
        NTSTATUS status;
        DWORD dwFindRec, IndexAllocOffset;
        ULONG i, dwLen, EntrySize;
        PINDEX_ENTRY pIndexEntry;
        PINDEX_BLOCK pIndexBlock;
        wchar_t szFileName[260];
        pIndexBlock = ExAllocatePoolWithTag(NonPagedPool, sizeof(INDEX_BLOCK), 'Bu4n');
        RtlZeroMemory(pIndexBlock , sizeof(INDEX_BLOCK));
        status = ScsiReadWriteDisk(g_pDr0DevObj, TRUE, (ULONG)(Sector), (PCHAR)pIndexBlock, sizeof(INDEX_BLOCK));
        if(!NT_SUCCESS(status))
        {
                ExFreePool(pIndexBlock);
                return 0;
        }
        if(*(PULONG)pIndexBlock != magic_INDX)
        {
                ExFreePool(pIndexBlock);
                return 0;
        }
/*
Standard Index Header
Offset         Size         Description
0x00         4         Magic number 'INDX'
0x04         2         Offset to the Update Sequence
0x06         2         Size in words of the Update Sequence Number & Array (S)
0x08         8         $LogFile sequence number
0x10         8         VCN of this INDX buffer in the Index Allocation
0x18         4         Offset to the Index Entries (a)
0x1C         4         Size of Index Entries (a)
0x20         4         Allocated size of the Index Entries (a)
0x24         1         1 if not leaf node (b)
0x25         3         Padding (always zero)
0x28         2         Update sequence
0x2A         2S-2         Update sequence array

(a) These values are relative to 0x18

(b) Has children
*/
        //_asm int 3;
        EntrySize = pIndexBlock->TotalEntrySize;
        pBuffer = ExAllocatePoolWithTag(NonPagedPool, 0x18 + pIndexBlock->EntryOffset + EntrySize, 'Bu4n');
        if (pBuffer == NULL)
        {
                ExFreePool(pIndexBlock);
                return 0;
        }
        //一次失败的调用。。。造成第二次不返回的调用。。。
        status = ScsiReadWriteDisk(g_pDr0DevObj, TRUE, (ULONG)(Sector), pBuffer, 0x18 + pIndexBlock->EntryOffset + EntrySize);
        ExFreePool(pIndexBlock);
        pIndexBlock = NULL;
        if(!NT_SUCCESS(status))
        {
                ExFreePool(pBuffer);
                return 0;
        }
        pIndexBlock = (PINDEX_BLOCK)pBuffer;
        pIndexEntry = (PINDEX_ENTRY)(pBuffer + 0x18 + pIndexBlock->EntryOffset);
        while((ULONG)pIndexEntry < (ULONG)pBuffer + EntrySize + pIndexBlock->EntryOffset)
        {
                if(pIndexEntry->Flags & 2) //END标志
                {
                        if(pIndexEntry->Flags & 1) //SUBNOD标志
                        {
                                /*IndexAllocOffset = *(PULONG)(pIndexEntry->Size - 8 + (ULONG)pIndexEntry);
                                dwFindRec = FindSplice(IndexRootRecordSector + IndexAllocOffset * 8, splice, IndexRootRecordSector);
                                if(dwFindRec != 0)
                                {
                                        return dwFindRec;
                                }*/
                        }
                        dprintf("Index Allocate END_NOD\r\n");
                        break;
                }
                else
                {
                        RtlZeroMemory(szFileName, 260 * sizeof(wchar_t));
                        RtlCopyMemory(szFileName, (PCHAR)((ULONG)pIndexEntry + 0x52), *(BYTE *)((ULONG)pIndexEntry + 0x50) * sizeof(wchar_t));
                        if(wcscmp(_wcsupr(szFileName), splice) == 0)
                        {
                                //Sector
                                dprintf("Find %S MftRec:%d\r\n", szFileName, *(PULONG)pIndexEntry);
                                return *(PULONG)pIndexEntry;
                        }
                        dprintf("%S\r\n", szFileName);
                }
                (ULONG)pIndexEntry += pIndexEntry->Size;
        }
        ExFreePool(pBuffer);
        return 0;
}

DWORD ParsePath(wchar_t *splice, DWORD Sector, PPARTITION_INFO pPartition)
//splice需要查找的文件名称
//sector父文件夹的MFT中的记录所在的扇区
//文件pPartition所在逻辑卷分区的信息
{
        NTSTATUS status;
        PMFT_RECORD pMftRecord;
        PATTRIBUTE pAttr;
        PINDEX_ROOT pIndexRoot;
        PINDEX_HEAD pIndexHead;
        PINDEX_ENTRY pIndexEntry;
        PNONRESIDENT_ATTRIBUTE pIndexAlloc;
        ULONG IndexRecordSector, IndexRootRecordSector, dwLength, IndexAllocOffset = 0;
        PCHAR pData;
        DWORD i, dwFindRet, dwDataRunSizeRemain;
        int DataRunOffset = 0, nOffset;
        wchar_t szFileName[260];
        status = STATUS_UNSUCCESSFUL;
        pMftRecord = ExAllocatePoolWithTag(NonPagedPool, 0x400, 'Bu4n');
        if(pMftRecord == NULL)
        {
                status = STATUS_UNSUCCESSFUL;
                goto END;
        }
        status = ScsiReadWriteDisk(g_pDr0DevObj, TRUE, Sector, (PUCHAR)pMftRecord, 0x200 * 2);
        if(!NT_SUCCESS(status))
        {
                goto END;
        }
        //解析IndexRoot属性
        pAttr = FindAttribute(pMftRecord, AttributeIndexRoot);
        if(pAttr == NULL)
        {
                goto END;
        }
        pIndexHead = (PINDEX_HEAD)((ULONG)pAttr + sizeof(INDEX_ROOT) + GetAttributeHeadSize(pAttr));
        pIndexEntry = (PINDEX_ENTRY)((ULONG)pIndexHead + pIndexHead->EntryOffset);
        //遍历IndexRoot中的IndexEntry
        while((ULONG)pIndexEntry < pIndexHead->TotalEntrySize + (ULONG)pIndexHead)
        {
                if(pIndexEntry->Flags & 0x2)
                {
                        if(pIndexEntry->Flags & 0x1)
                        {
                                IndexAllocOffset = *(PULONG)(pIndexEntry->Size - 8 + (ULONG)pIndexEntry);
                        }
                        dprintf("IndexRoot END_ENTRY IndexOffset:%d\r\n", IndexAllocOffset);
                        break;
                }
                else
                {
                        RtlZeroMemory(szFileName, 260 * sizeof(wchar_t));
                        RtlCopyMemory(szFileName, (PCHAR)((ULONG)pIndexEntry + 0x52), *(BYTE *)(pIndexEntry + 0x50) * sizeof(wchar_t));
                        dprintf("%S\r\n", szFileName);
                        if(wcscmp(_wcsupr(szFileName), splice) == 0)
                        {
                                dprintf("Find %S MftRec:%d\r\n", szFileName, *(PULONG)pIndexEntry);
                                dwFindRet = *(PULONG)pIndexEntry;
                                return pPartition->MFT + dwFindRet * 2;
                        }
                        (ULONG)pIndexEntry += pIndexEntry->Size;
                }
        }
        if(!(pIndexEntry->Flags & 0x1))
        {
                goto END;
        }
        //计算IndexAllocate指向的IndexRecord所在的扇区

        //FindAttribute根据Type从MFT中找到属性头的位置返回指向其的指针
        pIndexAlloc = (PNONRESIDENT_ATTRIBUTE)FindAttribute(pMftRecord, AttributeIndexAllocation);
        if(pIndexAlloc == NULL)
        {
                goto END;
        }
        //DataRun数据的指针
        pData = (PCHAR)(ULONG)pIndexAlloc + pIndexAlloc->RunArrayOffset;
        //DataRun数据的占用字节
        dwDataRunSizeRemain = pIndexAlloc->DataSize;
        //dwSize = pIndexAlloc->AllocatedSize;
        //解析DataRun获得Size LCN或VCN。。DataRunOffet是下一个DataRun的偏移
        DataRunOffset = ParseDataRun(pData, &nOffset, &dwLength);
        if(DataRunOffset == -1 || DataRunOffset == 0)
        {
                goto END;
        }
        //第一个DataRun指向的 IndexBlock数据的开始扇区
        IndexRootRecordSector = pPartition->StartLBA + nOffset * 8;
        for(IndexRecordSector = pPartition->StartLBA + nOffset * 8; nOffset != 0; IndexRecordSector += nOffset * 8)
        {
                //逐个簇遍历IndexBlock中的IndexEntry
                for(i = 0; i < dwLength; i++)
                {
                        //FindSplice从一个IndexBlock中查找指定的文件名
                        dwFindRet = FindSplice(IndexRecordSector + i * 8, splice, IndexRootRecordSector);
                        if(dwFindRet != 0)
                        {
                                return pPartition->MFT + dwFindRet * 2;
                        }
                }
                pData += DataRunOffset;
                //解析下一个DataRun
                DataRunOffset = ParseDataRun(pData, &nOffset, &dwLength);
                if(DataRunOffset == -1)
                {
                        goto END;
                }
        }
        //IndexRecordSector = (*(PLONG)(&pData[1]) >> 4) * 8 + pPartition->StartLBA;
        //ParseDir(IndexRecordSector);
        //if()
        //NextAllocateGrabFromIndexRoot
        //if(IndexAllocOffset != 0)
        //{
        //        //IndexRecordSector += IndexAllocOffset * 8;
        //        dwFindRet = FindSplice(IndexRootRecordSector + IndexAllocOffset * 8, splice, IndexRootRecordSector);
        //        if(dwFindRet != 0)
        //        {
        //                return pPartition->MFT + dwFindRet * 2;
        //        }
        //}
        status = 0;
END:
        if(pMftRecord != NULL)
        {
                ExFreePool(pMftRecord);
        }
        return status;
}

void FormatPath(wchar_t * lpPath, DWORD length)
{
        if(wcsstr(lpPath, L"\\SYSTEMROOT\\") != NULL)
        {
                RtlCopyMemory(lpPath, L"C:\\WINDOWS", 10 * sizeof(wchar_t));
                //RtlMoveMemory(lpPath, lpPath + 3, length - 12 * sizeof(wchar_t));
        }
        return;
}

wchar_t * SplitPath(wchar_t * RemainPath, wchar_t * splice)
{
        wchar_t * EndPath;
        DWORD dwLen;
        RemainPath++;
        EndPath = wcschr(RemainPath, L'\\');
        if(EndPath == NULL)
        {
                wcscpy(splice, RemainPath);
                return 0;
        }
        else
        {
                dwLen = (ULONG)EndPath - (ULONG)RemainPath;
                RtlZeroMemory(splice, dwLen + 2);
                RtlCopyMemory(splice, RemainPath, dwLen);
                return EndPath;
        }
}

//////////////////////////////////////////////////////////////////////////
//
//               这个是我想要完成的删除功能的函数。现在还没写删除的代码。。先能找到就不错
//
////////////////////////////////////////////////////////////////////////
NTSTATUS MyIoDeleteFile(PUNICODE_STRING pFilePath)
{
        wchar_t *lpPathBuffer, *RemainPath, szSplice[260];
        DWORD PartitionNumber;
        DWORD sector;
        //__asm int 3;
        lpPathBuffer = ExAllocatePoolWithTag(NonPagedPool, pFilePath->Length + 2, 'Bu4n');
        RtlZeroMemory(lpPathBuffer, pFilePath->Length + 2);
        RtlCopyMemory(lpPathBuffer, pFilePath->Buffer, pFilePath->Length);
        _wcsupr(lpPathBuffer);
        FormatPath(lpPathBuffer, pFilePath->Length);
        PartitionNumber = *lpPathBuffer - L'C';
        RemainPath = lpPathBuffer + 2;
        sector = g_PartitionInfo[PartitionNumber].MFT + 5 * 2;
        //ParseRootDir(g_PartitionInfo + PartitionNumber);
        __asm int 3;
        while(RemainPath != NULL)
        {
                 //从RemaindPath截取一个/ / 之间的部分。。放到splice中
                RemainPath = SplitPath(RemainPath, szSplice);
                sector = ParsePath(szSplice, sector, g_PartitionInfo + PartitionNumber);
                if(sector == 0)
                {
                        return STATUS_UNSUCCESSFUL;
                }
        }
        return STATUS_SUCCESS;
}
2013-5-7 19:49
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
3
IndexRoot里给的vcn不是摆设,是btree的root,应该从这个节点开始解析起。。。第一个vcn不是btree的根节点,你从这个开始解析肯定漏文件,而且列出来的文件也不会是按照$UpCase字符排序的顺序排列的
2013-5-7 20:01
0
雪    币: 2015
活跃值: (902)
能力值: ( LV12,RANK:1000 )
在线值:
发帖
回帖
粉丝
4
本质是数据结构没搞懂
2013-5-7 20:21
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
嗯。。因为我不懂这个b+树什么意思。。这个解析方法。。完全是。。根据winhex看到的结果猜出来的
2013-5-7 20:52
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
IndexRoot里的这个vcn都很小啊。。我把这个当做。。是lcn了当做子节点了。。我去看看怎么从这开始解析。。thanks...
2013-5-7 20:55
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
我刚才。。去试了。。半天。。搞不定这个Root....您的意思是不是。。如果从Root开始解析。。然后Root的子节点。。然后再子节点 (当然如果有子节点的话)。这个流程下来。就能遍历全部了。。如果是这样的话。。我现在最大的问题。。是Root的 vcn是怎么来的。。比如我举例的地方。。IndexRoot的最后一个节点里的子节点lcn 或者是vcn是个6。。这个6是相对与什么的偏移呢。我把这个理解为相对于第一个DataRun现在看来肯定是错的。但是我找了半天。也没发现这个是相对与什么的
2013-5-7 21:49
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
8
从datarun可以得到vcn到lcn是一一对应的序列,vcn为6,可以算出对应的lcn,这个lcn就是你第一批需要解析的IndexEntry。如果目录比较大,通常情况下第一批的indexentry都会有sub-node,然后往下解析就行了。http://0cch.net/ntfsdoc/attributes/index_allocation.html
2013-5-7 23:06
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
太感谢了。。我就是搞不懂这的具体细节。。就是DataRun中找第一批IndexEntry中的的lcn..
DataRun不是说明了块开始簇和块大小么。。这个一一对应。。是个什么意思
我的理解是
比如我这个例子的06是 第一个DataRun的size是7那么这个IndexEntry就在第一个块内
计算lcn = 第一个块的LCN + 06   。。。。我这个。。根据我用Winhex得到的记过看来是不对的。。。我就是想知道这个怎么算。。我也看了ntfsdoc...但是看来理解有偏差。。
2013-5-7 23:23
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
10
找lcn的方法是对的target_lcn = root_vcn - vcn_base + lcn_base。至于你说winhex看的不对,要么是你找lcn出了问题,要么是你看错了。
2013-5-8 00:16
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
好的。。那如果IndexRoot的子节点就没有子节点了呢。。。很明显没有目录没有这么小。。是不是就是找错了。。lcn...
2013-5-8 00:21
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
12
按你解析的,既然indexroot指明vcn是6,那么这个cluster的indexentry肯定有子节点,你没看到证明找错了。
2013-5-8 00:29
0
雪    币: 84
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
谢了。。你说的很明白了。。我再自己琢磨一下吧。。
2013-5-8 00:31
0
游客
登录 | 注册 方可回帖
返回
//