Win7 64位的SSDTHOOK(2)---64位SSDT hook的实现

Hook之前要干掉PG:http://www.m5home.com/bbs/thread-5893-1-1.html

上篇文章知道了寻找SSDT表的方法,这篇记录一下如何实现SSDT表的Hook。

下面以Hook NtOpenProcess为例,之前我查SSDT表发现NtOpenProcess函数的标号为35,用XT等工具也能查看。

废话不多说,上代码。


首先感谢老大(Tesla.Angela)对我的帮助  
  
//相关声明  
__int64 __readmsr(int register);  
unsigned __int64 __readcr0(void);  
void __writecr0(   
    unsigned __int64 Data   
    );  
void _disable(void);  
void _enable(void);  
  
//_SYSTEM_SERVICE_TABLE结构声明  
typedef struct _SYSTEM_SERVICE_TABLE{  
    PVOID       ServiceTableBase;   
    PVOID       ServiceCounterTableBase;   
    ULONGLONG   NumberOfServices;   
    PVOID       ParamTableBase;   
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;  
  
//_SERVICE_DESCRIPTOR_TABLE结构声明  
typedef struct _SERVICE_DESCRIPTOR_TABLE{  
    SYSTEM_SERVICE_TABLE ntoskrnl;  // ntoskrnl.exe (native api)  
    SYSTEM_SERVICE_TABLE win32k;    // win32k.sys   (gdi/user)  
    SYSTEM_SERVICE_TABLE Table3;    // not used  
    SYSTEM_SERVICE_TABLE Table4;    // not used  
}SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE;  
  
//声明要寻找进程名用的函数  
NTKERNELAPI UCHAR * PsGetProcessImageFileName(PEPROCESS Process);  
  
//定义NTOPENPROCESS  
typedef NTSTATUS (__stdcall *NTOPENPROCESS)(OUT PHANDLE  ProcessHandle,  
                                    IN ACCESS_MASK  DesiredAccess,  
                                    IN POBJECT_ATTRIBUTES  ObjectAttributes,   
                                    IN OPTIONAL PCLIENT_ID  ClientId);  
NTOPENPROCESS OldOpenProcess = NULL;  
ULONG OldTpVal;  
  
//定义自己的NtOpenProcess  
NTSTATUS __stdcall Fake_NtOpenProcess(OUT PHANDLE  ProcessHandle,  
                                       IN ACCESS_MASK  DesiredAccess,  
                                       IN POBJECT_ATTRIBUTES  ObjectAttributes,   
                                       IN OPTIONAL PCLIENT_ID  ClientId)  
{  
    PEPROCESS process = NULL;  
    NTSTATUS st = ObReferenceObjectByHandle(ClientId->processid,0,*PsProcessType,KernelMode,&process,NULL);  
    DbgPrint("进入HOOK函数.\n");  
    if (NT_SUCCESS(st))  
    {  
        if (!_stricmp((char*)PsGetProcessImageFileName(process),"CrackMe3.exe"))  
        {  
              
            return STATUS_ACCESS_DENIED;  
        }  
        else  
        {  
            return OldOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);  
        }   
    }  
    else  
    {  
        return STATUS_ACCESS_DENIED;  
    }  
}  
  
//关闭页面保护  
KIRQL WPOFFx64()  
{  
    KIRQL irql=KeRaiseIrqlToDpcLevel();  
    UINT64 cr0=__readcr0();  
    cr0 &= 0xfffffffffffeffff;  
    __writecr0(cr0);  
    _disable();  
    return irql;  
}  
//开启页面保护  
void WPONx64(KIRQL irql)  
{  
    UINT64 cr0=__readcr0();  
    cr0 |= 0x10000;  
    _enable();  
    __writecr0(cr0);  
    KeLowerIrql(irql);  
}  
  
//老外定位KeServiceDescriptorTable的方法  
ULONGLONG GetKeServiceDescriptorTable64()   
{  
    char KiSystemServiceStart_pattern[] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";   //特征码  
    ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;  
    ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;  
    UNICODE_STRING Symbol;  
    ULONGLONG i, tbl_address, b;  
    for (i = 0; i < CodeScanEnd - CodeScanStart; i++)  
    {  
        if (!memcmp((char*)(ULONGLONG)CodeScanStart +i, (char*)KiSystemServiceStart_pattern,13))  
        {   
            for (b = 0; b < 50; b++)  
            {  
                tbl_address = ((ULONGLONG)CodeScanStart+i+b);  
                if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)  
                    return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);  
            }  
        }  
    }  
    return 0;  
}  
  
//根据KeServiceDescriptorTable找到SSDT基址  
PULONG GetSSDTBaseAddress()  
{  
    PULONG addr = NULL;  
    PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64();  
    addr = (PULONG)(ssdt->ServiceTableBase);  
    return addr;  
}  
  
//根据标号找到SSDT表中函数的地址  
ULONGLONG GetFuncAddr(ULONG id)  
{  
    LONG dwtmp = 0;  
    ULONGLONG addr = 0;  
    PULONG stb = NULL;  
    stb = GetSSDTBaseAddress();  
    dwtmp = stb[id];  
    dwtmp = dwtmp >> 4;  
    addr = (LONGLONG)dwtmp + (ULONGLONG)stb;  
    DbgPrint("SSDT TABLE BASEADDRESS:%llx",addr);  
    return addr;  
}  
  
//设置函数的偏移地址,注意其中参数的处理。低四位放了参数个数减4个参数。如果参数小于等于4的时候为0  
#define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1  
#define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0  
#define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0  
ULONG GetOffsetAddress(ULONGLONG FuncAddr, CHAR paramCount)  
{  
    LONG dwtmp = 0,i;  
    CHAR b = 0, bits[4] = {0};  
    PULONG stb = NULL;  
    stb = GetSSDTBaseAddress();  
    dwtmp = (LONG)(FuncAddr - (ULONGLONG)stb);  
    dwtmp = dwtmp << 4;  
    if (paramCount>4)  
    {  
        paramCount = paramCount - 4;  
    }  
    else  
    {  
        paramCount = 0;  
    }  
    memcpy(&b,&dwtmp,1);  
    for (i=0;i<4;i++)  
    {  
        bits[i] = GETBIT(paramCount,i);  
        if (bits[i])  
        {  
            SETBIT(b,i);  
        }  
        else  
        {  
            CLRBIT(b,i);  
        }  
    }  
    memcpy(&dwtmp,&b,1);  
    return dwtmp;  
}  
  
//内核中用不到的方法,二次跳转用(自己的NtOpenProcess跳到KeBugCheckEx函数,然后再KeBugCheckEx函数跳到要Hook的NtOpenProcess)  
VOID FuckKeBugCheckEx()  
{  
    KIRQL irql;  
    ULONGLONG myfun;  
    UCHAR jmp_code[]="\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";  
    myfun=(ULONGLONG)Fake_NtOpenProcess;  
    memcpy(jmp_code+6,&myfun,8);  
    irql=WPOFFx64();  
    memset(KeBugCheckEx,0x90,15);  
    memcpy(KeBugCheckEx,jmp_code,14);  
    WPONx64(irql);  
}  
  
//Hook ssdt  
VOID HookSSDT()  
{  
    KIRQL irql;  
    LONG dwtmp = 0;  
    PULONG stb = NULL;  
    //1.get old address  
    OldOpenProcess = (NTOPENPROCESS)GetFuncAddr(35);  
    DbgPrint("Old_NtOpenProcess:%llx",(ULONGLONG)OldOpenProcess);  
    //2.show new address  
    stb = GetSSDTBaseAddress();  
    //3.get offset value  
    dwtmp = GetOffsetAddress((ULONGLONG)KeBugCheckEx,4);  
    //set kebugcheckex  
    FuckKeBugCheckEx();  
    //4.record  old offset  value  
    OldTpVal = stb[35];  
    irql = WPOFFx64();  
    stb[35] = GetOffsetAddress((ULONGLONG)KeBugCheckEx,2);  
    WPONx64(irql);  
    DbgPrint("KeBugCheckEx:%llx",(ULONGLONG)KeBugCheckEx);  
    DbgPrint("New_NtOpenProcess:%llx",GetFuncAddr(35));  
}  
  
//UN hook  
VOID UnhookSSDT()  
{  
    KIRQL irql;  
    PULONG stb=NULL;  
    stb = GetSSDTBaseAddress();  
    //老函数的地址复制回来  
    irql=WPOFFx64();  
    stb[35]=OldTpVal;  
    WPONx64(irql);  
}  

相关解释:

1.为什么要二次跳转?

WIN64内核里的每个驱动都不在同一个4GB里,4字节的整数只能表示4GB的范围,所以不管怎么修改这个4字节都不会跳到你的代理函数,因为你的驱动不可能跟NTOSKRNL在同一个4GB里面。

2.参数的处理:

函数地址的低四位存放了函数参数个数减4的数字,如果参数为5,那么低四位的数字为1,如果参数个数小于等于4个,低四位的数位0,可以再WINDBG里面查看到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值