重载ShadowSSDT

系统环境:WIN732位
重载内容:两张系统服务调度表 ,传说的SSDT和ShadowSSDT
作用:通过重载内核,可以饶过各大著名驱动保护的HOOK.极少数例外.
重载方式:挂勾系统三环和零环的主要通道KiFastCallEntry,然后改变自己的进程,通过新内核.
实现环境:NT式驱动环境
核心步骤;一,以PE文件在内存中的对齐方式将内核文件和WIN32K.sys读取到内存中. 
步骤二;以原始内核文件为依据,修复基址表重定位表.
步骤三;重新构建自己的SSDT表 
步骤四;定位KiFastCallEntry 里最佳HOOK点,
步骤五;实现自己的过滤函数.
步骤六;重载WIN32K.sys的时候,必须以GUI线程的方式运行,如此最好在用户层实现一个与驱动通信的程序.
原理是,驱动程序中的派遣函数其实全都是运行在应用程序的上下文环境中.用户层调用驱动的派遣例程就能成功访问WIN32K.sys里的内容


//核心代码开始/
//变量区头文件内容
typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase; //仅适用于checked build版本
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
//全局变量区
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
PServiceDescriptorTableEntry_t  KeServiceDescriptorTableShadow=NULL ;
PServiceDescriptorTableEntry_t  PNewSSDT; //新SSDT表变量
PServiceDescriptorTableEntry_t  PNewShadowSSDT; //新ShadowSSDT表变量
ULONG kerAddress;  //枚举驱动后保存原始内核模块用的变量
ULONG win32kAddress;//枚举驱动后保存原始WIN32K.sys内核模块用的变量
PVOID NewKernalVirualAddress = NULL;//内核模块重载的新地址。
PVOID NewWin32kAddress = NULL;//shadowSSDT模块重载的新地址。
UCHAR *Faddress;//KiFastCall  hook点 
UCHAR backHookCode[5] = { 0 }; //KiFastCallEntry Hook 还原代码
ULONG HookbackAddress;//KiFastCallEntry Hook 返回跳转地址
ULONG ShadowBitAddr = 0;
char protect1[128] = { 0 };//要饶过旧内核的进程1,此变量用来接受用户层传进来的进程名,此进程将走新内核
char protect2[128] = { 0 };//要饶过旧内核的进程2,此变量用来接受用户层传进来的进程名,此进程将走新内核
char protect3[128] = { 0 };//要饶过旧内核的进程3,此变量用来接受用户层传进来的进程名,此进程将走新内核
void PageProtectOn()//恢复内存保护 
{
        __asm{
                mov eax, cr0
                        or eax, 10000h
                        mov cr0, eax
        }
}
void PageProtectOff()//去掉内存保护
{
        __asm{
                mov eax, cr0
                        and eax, not 10000h
                        mov cr0, eax

        }


///重载内核主要头文件内容/// 
#ifndef  NTh
#define  NTh
#include <ntddk.h>
#include <EveryHead.h>
#endif
#include <ntimage.h>
/*此头文件实现重载内核功能,主要功能模块有如下;将磁盘中的内核文件按PE对齐值装载到内存,
修复需要重定位的数据,构建新SSDT表,枚举内核驱动模块,HOOK KiFastCallEntry,构造自己在KiFastCallEntry中的过滤函数
*/

///枚举内核驱动模块Start
typedef struct _LDR_DATA_TABLE_ENTRY {
        LIST_ENTRY InLoadOrderLinks;
        LIST_ENTRY InMemoryOrderLinks;
        LIST_ENTRY InInitializationOrderLinks;
        PVOID DllBase;
        PVOID EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING FullDllName;
        UNICODE_STRING BaseDllName;
        ULONG Flags;
        USHORT LoadCount;
        USHORT TlsIndex;
        union {
                LIST_ENTRY HashLinks;
                struct {
                        PVOID SectionPointer;
                        ULONG CheckSum;
                };
        };
        union {
                struct {
                        ULONG TimeDateStamp;
                };
                struct {
                        PVOID LoadedImports;
                };
        };
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

/*函数原形;ULONG EnumDriver(PDRIVER_OBJECT pDriverObject)
功能:枚举内核驱动,并返回指定名称的驱动模块地址
参数1:当前驱动对象的指针
返回是内核驱动模块L"ntoskrnl.exe"的地址
*/
ULONG EnumDriver(PDRIVER_OBJECT pDriverObject,PWCHAR oriAddress)
{
        UNICODE_STRING findName;
        RtlInitUnicodeString(&findName, oriAddress);
        PLDR_DATA_TABLE_ENTRY DTableEntry, TempTable;
        DTableEntry = pDriverObject->DriverSection;
        PLIST_ENTRY plist = DTableEntry->InLoadOrderLinks.Flink;
        while (plist != DTableEntry)
        {
                TempTable = (PLDR_DATA_TABLE_ENTRY)plist;
                KdPrint(("%wZ--%x", &TempTable->BaseDllName, TempTable->DllBase));
                if (RtlCompareUnicodeString(&TempTable->BaseDllName, &findName, FALSE) == 0)
                {
                        KdPrint(("find DLLbase is %x", TempTable->DllBase));
                        return TempTable->DllBase;
                }

                plist = plist->Flink;
        }
        return 0;
}




///Read File To Memory Start

/*ReadFileToMemory(wchar_t *FileName, PVOID *OutAddress)
第一个参数是要打开的文件名,包含全路径,
第二个参数是磁盘文件读取到内存后的地址,是一个传出的参数。
*/
#define __Max(a,b) a>b?a:b
NTSTATUS ReadFileToMemory(WCHAR *FileName, PVOID *OutAddress)
{
        NTSTATUS status;
        if (!MmIsAddressValid(FileName))
        {
                KdPrint(("file name valid"));
                return STATUS_UNSUCCESSFUL;
        }
        //第一步打开要读到内存的文件,获得文件对象的句柄
        //ZwCreateFile 整个调用如下步骤
        HANDLE hFile;
        OBJECT_ATTRIBUTES ObjAttrutes;
        IO_STATUS_BLOCK ioStatusBlock;
        UNICODE_STRING unicodeFileName;
        //初始化ZwCreateFile的参数
        RtlInitUnicodeString(&unicodeFileName, FileName);
        InitializeObjectAttributes(&ObjAttrutes, &unicodeFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
        status = ZwCreateFile(&hFile,FILE_ALL_ACCESS,&ObjAttrutes,&ioStatusBlock,NULL,
                FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN,
                FILE_NON_DIRECTORY_FILE,
                NULL,0);
        //ZwCreateFile调用结束,以上步骤是固定模式,以后可以直接复制这段。
        if (!NT_SUCCESS(status))
        {
                KdPrint(("ZwCreateFile failed error code is %x",status));
                return status;
        }
        //第二步,获得文件句柄之后,开始读取PE文件的DOS头,NT头和区块表到内存
        IMAGE_DOS_HEADER ImageDosHeader;
        LARGE_INTEGER FileOffset;
        //读取DOS头
        FileOffset.QuadPart = 0;
        status = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatusBlock,&ImageDosHeader,
                sizeof(IMAGE_DOS_HEADER),&FileOffset,NULL);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("read image dos header failed code is%x\n", status));
                ZwClose(hFile);
                return status;
        }
        //读取NT头
        IMAGE_NT_HEADERS ImageNTHeader;
        FileOffset.QuadPart = ImageDosHeader.e_lfanew;
        status = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatusBlock, &ImageNTHeader, sizeof(IMAGE_NT_HEADERS),
                &FileOffset, NULL);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("read nt header failed error code is %x\n", status));
                ZwClose(hFile);
                return status;
        }
        //读取区块表
        FileOffset.QuadPart = ImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS); //设置文件指针
        IMAGE_SECTION_HEADER *pImageSectionHeader;
        //申请一块和区块表大小相同的内存,然后将区块表复制到这块内存中
        pImageSectionHeader = ExAllocatePool(NonPagedPool, 
                sizeof(IMAGE_SECTION_HEADER)*ImageNTHeader.FileHeader.NumberOfSections);
        if (pImageSectionHeader==NULL)
        {
                KdPrint(("section exallocate pool  failed"));
                ZwClose(hFile);
                return STATUS_UNSUCCESSFUL;
        }
        status = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatusBlock, pImageSectionHeader,
                sizeof(IMAGE_SECTION_HEADER)*ImageNTHeader.FileHeader.NumberOfSections, &FileOffset,NULL);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("read sections failed,error coed %x\n",status));
                ZwClose(hFile);
                ExFreePool(pImageSectionHeader);
                return STATUS_UNSUCCESSFUL;
        }

        //第三步,重新申请一块内存,将之前的DOS头,NT头,区块表和真正的区块组织成一个连续整体。
        //DOS头,NT头和区块表在磁盘文件中的对齐和内存的对齐是没有区别的,直接复制。
        PVOID FullVirtualAddress = ExAllocatePool(NonPagedPool, ImageNTHeader.OptionalHeader.SizeOfImage);
        if (FullVirtualAddress==0)
        {
                KdPrint(("allocate memory failed"));
                ZwClose(hFile);
                ExFreePool(pImageSectionHeader);
                return STATUS_UNSUCCESSFUL;
        }
        memset(FullVirtualAddress, 0, ImageNTHeader.OptionalHeader.SizeOfImage);//清零新申请的内存
        //申请到内存开始复制
        RtlCopyMemory(FullVirtualAddress,&ImageDosHeader,sizeof(IMAGE_DOS_HEADER));//复制DOS头
        RtlCopyMemory((PVOID)((ULONG)FullVirtualAddress+ImageDosHeader.e_lfanew),&ImageNTHeader,sizeof(IMAGE_NT_HEADERS));//复制NT头
        //复制区块表
        RtlCopyMemory((PVOID)((ULONG)FullVirtualAddress + ImageDosHeader.e_lfanew+sizeof(IMAGE_NT_HEADERS)),
                pImageSectionHeader,sizeof(IMAGE_SECTION_HEADER)*ImageNTHeader.FileHeader.NumberOfSections);
        //用FOR循环复制真正的区块表,真正的区块表一定要按内存对齐值读取的内存。
        ULONG SecVirualAddress;
        ULONG dataRaw;
        for (ULONG i = 0; i < ImageNTHeader.FileHeader.NumberOfSections; i++)
        {
                SecVirualAddress = pImageSectionHeader .VirtualAddress+(ULONG)FullVirtualAddress;
                dataRaw = max(pImageSectionHeader .SizeOfRawData, pImageSectionHeader .Misc.VirtualSize);
                FileOffset.QuadPart = pImageSectionHeader .PointerToRawData;
                status = ZwReadFile(hFile, NULL, NULL, NULL, &ioStatusBlock, (PVOID)SecVirualAddress,dataRaw,&FileOffset,NULL);
                if (!NT_SUCCESS(status))
                {
                        KdPrint(("read file failed in section[%d]\n", i));
                        ExFreePool(FullVirtualAddress);
                        ExFreePool(pImageSectionHeader);
                        ZwClose(hFile);
                        return status;
                }
        }
        KdPrint(("read file to memory success address is%x\n", FullVirtualAddress));
        *OutAddress = FullVirtualAddress;
}



/修复基址重定位表中的数据和构建新SSDT表开始///
/*函数原型 VOID RelocationModule(PVOID PNewImageAddres,PVOID POriginalAddress)
功能;修复重定位表里的数据
参数一,需要修复重定位数据的模块地址
参数二,原始内核模块在内存中的地址,因为新内核不能完全替代原始内核,许多原始内核数据在初始化时已
决定,新内核必须以原始内核基址为依据进行重定位,所以这里第二个参数传入原始内核地址。
*/
VOID RelocationModuleAndBuildSSDT(PVOID PNewImageAddres,PVOID POriginalAddress)
{
        
        PIMAGE_DOS_HEADER       pImageDosHeader;
        PIMAGE_NT_HEADERS       pImageNTHeader;
        IMAGE_DATA_DIRECTORY    imagedatadectory;
        PIMAGE_BASE_RELOCATION  pimagebaserelocation;
        USHORT                  typevalue;
        ULONG                   RelocationAddress;
        pImageDosHeader = PNewImageAddres;
        pImageNTHeader = (ULONG)PNewImageAddres + pImageDosHeader->e_lfanew;
        ULONG  RelocOffset = (ULONG)POriginalAddress - pImageNTHeader->OptionalHeader.ImageBase; //重定位数据偏移量计算
        imagedatadectory =pImageNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
        pimagebaserelocation = (ULONG)PNewImageAddres + imagedatadectory.VirtualAddress;

        /*重定位块PIMAGE_BASE_RELOCATION的.VirtualAddres成员不等于零时进行循环修复每一页中的重定位数据。
        重定位块是一个连续存放的数组,每一个重定位块的大小是不相同的,但却是已知的。一个重定位块只能修复一页内存中的重定位数据项,
        重定位块的数量是不确定的,最后以一个内容为零的重定位块作为结束标志,
        每一页内存中有多少个数据需要重定位也是不相等的,但是却是可以通过PIMAGE_BASE_RELOCATION的.SizeOfBlock- 8) / 2计算出来,
        用(SizeOfBlock-8)/2就可以得到每一页内存中有多少需要修复的数据,每一个重定位项占2字节,所以除2,减8是因为重定位块有两个成员
        PIMAGE_BASE_RELOCATION->VirtualAddress和PIMAGE_BASE_RELOCATION->SizeOfBlock各占四字节。
        重点内容是;第X+1重定位块的地址就等于第X重定位块的地址加上X.SizeOfBlock,依此类推就能得到所有重定位块的地址,
        */
        ULONG ab = 0;//计算有多少个需要修复的重定位块用的变量,每个重定位块有若干需要重定位的项
        while (pimagebaserelocation->VirtualAddress)
        {
                ULONG VirtualAddress = pimagebaserelocation->VirtualAddress+ (ULONG)PNewImageAddres;
                ULONG NumberOfReloc = (pimagebaserelocation->SizeOfBlock- 8) / 2;
                ab = ab + 1;
                for (ULONG index = 0; index < NumberOfReloc;index++)
                {
                        typevalue = pimagebaserelocation->TypeOffset[index];
                        if (typevalue>>12==IMAGE_REL_BASED_HIGHLOW)
                        {
                                RelocationAddress = (typevalue&0xfff) + pimagebaserelocation->VirtualAddress + (ULONG)PNewImageAddres;
                                if (!MmIsAddressValid(RelocationAddress))
                                {
                                        continue;
                                }
                                *(ULONG*)RelocationAddress += RelocOffset;
                        }

                }
        
                pimagebaserelocation = (ULONG)pimagebaserelocation +pimagebaserelocation->SizeOfBlock;
                //(ULONG)pimagebaserelocation +pimagebaserelocation->SizeOfBlock; 这句前面一定要叫个(ULONG)转换
                //我一开始没加,就一直出错。
                KdPrint(("修复第%d个重定位块\n",ab));
        }
        KdPrint(("Relocation base end"));        
}

///Build New SSDT start构造新SSDT表开始/
VOID buildSSDT(PVOID PNewImageAddres, PVOID POriginalAddress)
{
        ULONG  RelocOffset;
        PNewSSDT= ((ULONG)&KeServiceDescriptorTable - (ULONG)POriginalAddress) + (ULONG)PNewImageAddres;//获得新SSDT结构地址
        if (!MmIsAddressValid(PNewSSDT))
        {
                KdPrint(("new ssdt address is invalid"));
                return;
        }
        RelocOffset = (ULONG)PNewImageAddres - (ULONG)POriginalAddress;//定位偏移
        PNewSSDT->NumberOfServices = KeServiceDescriptorTable.NumberOfServices;//设置新SSDT表个数
        PNewSSDT->ServiceTableBase = ((ULONG)KeServiceDescriptorTable.ServiceTableBase - (ULONG)POriginalAddress) + (ULONG)PNewImageAddres;
        PNewSSDT->ParamTableBase = ((ULONG)KeServiceDescriptorTable.ParamTableBase - (ULONG)POriginalAddress) + (ULONG)PNewImageAddres;
        for (ULONG index = 0; index < KeServiceDescriptorTable.NumberOfServices; index++)//构建新SSDT表的TableBase
        {
                PNewSSDT->ServiceTableBase[index] += RelocOffset;
        }
        RtlCopyMemory(PNewSSDT->ParamTableBase, KeServiceDescriptorTable.ParamTableBase, KeServiceDescriptorTable.NumberOfServices*sizeof(char));
        KdPrint(("new ssdt build success address is%x\n", PNewSSDT));
}
///Build New ShadowSSDT start构造新ShadowSSDT表开始/
/*函数原型:buildShadowSSDT(PVOID PNewImageAddres, PVOID POriginalAddress)
参数一;新WIN32K.SYS的基地址。
参数二;原始ntkrnlpa.exe文件的基地址,切记,这里不能传入原始WIN32K.sys地址,因为KeServiceDescriptorTableShadow表依然在ntkrnlpa.exe内核文件中
参数三;原始WIN32K.sys模块基地址*/
VOID buildShadowSSDT(PVOID PNewImageAddres, PVOID POriginalAddress,PVOID origWin32kAddress)
{
        ULONG  RelocOffset;
        KeServiceDescriptorTableShadow =(ULONG)&KeServiceDescriptorTable+0x50;//WIN732位系统中KeServiceDescriptorTableShadow可以这样定位。
        PNewShadowSSDT = ((ULONG)KeServiceDescriptorTableShadow - (ULONG)POriginalAddress) + (ULONG)PNewImageAddres;//获得新SSDT结构地址
        if (!MmIsAddressValid(PNewSSDT))
        {
                KdPrint(("new ssdt address is invalid"));
                return;
        }
        RelocOffset = (ULONG)PNewImageAddres - (ULONG)origWin32kAddress;//定位偏移
        PNewShadowSSDT->NumberOfServices = KeServiceDescriptorTableShadow->NumberOfServices;//设置新SSDT表个数
        /*注意;KeServiceDescriptorTableShadow结构变量在(ULONG)&KeServiceDescriptorTable+0x50的地方,但是KeServiceDescriptorTableShadow->ServiceTableBase 却是在
        WIN32K.sys模块中。
        */
        PNewShadowSSDT->ServiceTableBase = ((ULONG)KeServiceDescriptorTableShadow->ServiceTableBase - (ULONG)origWin32kAddress) + (ULONG)PNewImageAddres;
        PNewShadowSSDT->ParamTableBase = ((ULONG)KeServiceDescriptorTableShadow->ParamTableBase - (ULONG)origWin32kAddress) + (ULONG)PNewImageAddres;
        for (ULONG index = 0; index < KeServiceDescriptorTableShadow->NumberOfServices; index++)//构建新SSDT表的TableBase
        {
                PNewShadowSSDT->ServiceTableBase[index] += RelocOffset;
        }
        RtlCopyMemory(PNewShadowSSDT->ParamTableBase, KeServiceDescriptorTableShadow->ParamTableBase, KeServiceDescriptorTableShadow->NumberOfServices*sizeof(char));
        KdPrint(("new shadow ssdt build success address is%x\n", PNewShadowSSDT));
}




/*开始构造自己在KiFastCallEntry中的过滤函数,这种过滤函数需要两个,第一个是裸函数,实现内容必须是汇编,第二个是功能函数,是将被第一个调用的
为什么这种过滤函数要用两个呢?因为第一个调用的是裸函数,裸函数的好处是不会破坏堆栈,不会产生额外的代码,但是它的坏处是只能用汇编实现内部代码,
不合适用高级语言接受参数或返回值等等,如果我们用汇编在这种裸函数内部实现过滤功能就太麻烦了,因此在第二个过滤函数中用高级语言实现我们的功能。
*/


VOID filterKiFastCall2(ULONG tablebase, ULONG indexs, ULONG OrigFuncAddress)
{
        
        if (tablebase == KeServiceDescriptorTable.ServiceTableBase)
        {
                //DbgBreakPoint();
                //KdPrint(("entry be hook fastcall\n"));
                if (strstr((char *)PsGetCurrentProcess() + 0x16c, protect1) != 0 || \
                        strstr((char*)PsGetCurrentProcess() + 0x16c, protect2) != 0 || \
                        strstr((char*)PsGetCurrentProcess() + 0x16c, protect3) != 0)
                {
                        //KdPrint(("ssdt find be protect process is %s\n",(char*)PsGetCurrentProcess()+0x16c));
                        return PNewSSDT->ServiceTableBase[indexs];
                        
                }
                
        }
        if (tablebase == KeServiceDescriptorTableShadow->ServiceTableBase) //过滤shadowssdt
        {
                
                //KdPrint(("entry be hook fastcall\n"));
                if (strstr((char *)PsGetCurrentProcess() + 0x16c, protect1) != 0 || \
                        strstr((char*)PsGetCurrentProcess() + 0x16c, protect2) != 0 || \
                        strstr((char*)PsGetCurrentProcess() + 0x16c, protect3) != 0)
                {
                        //KdPrint(("shadow ssdt find be protect process is %s\n", (char*)PsGetCurrentProcess() + 0x16c));
                        return PNewShadowSSDT->ServiceTableBase[indexs];

                }

        }
        
        return OrigFuncAddress;
}

__declspec(naked)
VOID filterKiFastCall1()  //第一个过滤函数简单调用第二个,用汇编传入参数。
{
        __asm{
            pushad  //切记,一定要pushad保存寄存器环境
                pushfd
                push edx
                push eax //系统调用的下标
                push edi //SSDT数组地址
                call filterKiFastCall2  //调用此函数会自动平衡堆栈,不需要有平衡堆栈的动作
                mov [esp+0x18],eax 
                popfd
                popad
                sub esp, ecx  //恢复被HOOK指令改变的五个字节
                shr ecx, 2
                jmp HookbackAddress  //跳转到HOOK点加五节字的地方
        }
}



/Hook KiFastCallEntry Start/
VOID HookKiFastCallEntry()
{
        /*首先获得KiFastCallEntry函数的地址,梦无极的教程是用栈回溯的方式获得此地址的,这里将用另一种更简单的方式
        CPU为了支持快速系统调用,另外增加了三个寄存器,其中MSR为0x176的寄存器就专门存放了KiFastCallEntry的地址,只要读取这个寄存器就可以
        获得KiFastCallEntry的地址了。切记是0x176,十六进制的MSR地址。
        */
        KdPrint(("start hook protect1=%s,protect2=%s,protect3=%s\n", protect1, protect2, protect3));

        ULONG KiFastCallAddress=0;
        __asm{
                mov ecx,0x176
                rdmsr
                mov KiFastCallAddress,eax  //这三句汇编代码已获得KiFastCallEntry的地址。
        }
        //开始特征码定位KiFastCallEntry内部最佳HOOK点
        /*
        83e5419e 8a0c10          mov     cl,byte ptr [eax+edx]
        83e541a1 8b1487          mov     edx,dword ptr [edi+eax*4]
        83e541a4 2be1            sub     esp,ecx
        83e541a6 c1e902          shr     ecx,2
        83e541a9 8bfc            mov     edi,esp
        */
        if (KiFastCallAddress==0)
        {
                KdPrint(("get kifastcall failed"));
                return;
        }
        Faddress=(UCHAR*)KiFastCallAddress;
        for (ULONG index = 0; index < 0x300;index++)
        {
                if (*Faddress == 0x2B &&*(Faddress + 1) == 0xE1 &&*(Faddress + 2) == 0xC1 &&
                        *(Faddress + 3) == 0xE9 &&*(Faddress + 4) == 0x02)
                {
                        KdPrint(("find best point address is%x\n", Faddress));
                        HookbackAddress = Faddress + 5;
                        break;
                }
                Faddress++;
        }
        
        /*特征码定位到最佳HOOK点之后,开始对它进行HOOK,让它跳转到我们自己的过滤函数中*/
        UCHAR hookCode[5] = { 0 };
        hookCode[0] = 0xe9;//JMP机器码是0xE9,CALL的机器码是0xe8
        *(ULONG*)&hookCode[1] = ((ULONG)filterKiFastCall1 - (ULONG)Faddress) - 5;
        PageProtectOff();
        RtlCopyMemory(backHookCode, Faddress, 5);
        RtlCopyMemory(Faddress, hookCode, 5);
        PageProtectOn();
}

VOID UnHookKiFastCall()//取消重载内核,释放新模块内存
{
        PageProtectOff();
        RtlCopyMemory(Faddress,backHookCode, 5);
        PageProtectOn();
        ExFreePool(NewKernalVirualAddress);
        ExFreePool(NewWin32kAddress);
}

驱动主文件内容,主要是派遣例程写法/
#include <FileOp.h>
#include "SSDTHook.h"
#include <ReLoadKernal.h>
#define stringBuffe  CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) //缓冲模式交互控制码
#define stringNeither CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_NEITHER,FILE_ANY_ACCESS)  //其它模式交互控制码
#define stringDirect CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_IN_DIRECT,FILE_ANY_ACCESS)//直接模式交互控制码
PDEVICE_OBJECT DeviceObejct;
PWCHAR inputBuffer;
PCHAR  protectBuffer;
NTSTATUS UnloadDriver(PDRIVER_OBJECT pDriverObject)
{
        NTSTATUS status;
        UNICODE_STRING SymbolicName;
        RtlInitUnicodeString(&SymbolicName, L"\\??\\AssociateDriver");
        status = IoDeleteSymbolicLink(&SymbolicName);
        if (pDriverObject->DeviceObject!=NULL)
        {
                IoDeleteDevice(pDriverObject->DeviceObject);
        }
        if (!NT_SUCCESS(status))
        {
                KdPrint(("unload driver failed"));
                return status;
        }
        //UnHookNtOpenProcess();//SSDTHook
        //UninlineHook();//inLineSSDTHook
        UnHookKiFastCall();
        KdPrint(("unload driver success"));
        return STATUS_SUCCESS;
}
//创建设备
NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
{
        NTSTATUS status;
        UNICODE_STRING DeviceName;
        UNICODE_STRING SymbolicName;
        RtlInitUnicodeString(&DeviceName, L"\\Device\\AssociateDevice");
        status = IoCreateDevice(pDriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObejct);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("Create Device failed"));
        }
        DeviceObejct->Flags |= DO_BUFFERED_IO;
        pDriverObject->Flags |= DO_BUFFERED_IO;
        RtlInitUnicodeString(&SymbolicName, L"\\??\\AssociateDriver");
        status=IoCreateSymbolicLink(&SymbolicName, &DeviceName);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("create symbolic link failed"));
                IoDeleteDevice(DeviceObejct);
                return status;
        }
        return STATUS_SUCCESS;
}
NTSTATUS DispatchMajor(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
        PWCHAR buffer = L"来自驱动,缓冲模式";
        PWCHAR direct = L"来自驱动, 直接模式";
        PWCHAR neither = L"来自驱动,   其它模式";
        ULONG wr = 0;
        //得到当前栈指针
        PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
        ULONG mf = stack->MajorFunction;//得到当前IRP栈中的派遣函数,不是派遣函数的数组.
        KdPrint(("entry device dispatch"));
        switch (mf)
        {
        case IRP_MJ_DEVICE_CONTROL:
                KdPrint(("entry device control"));
                //得到输入缓冲区大小
                ULONG cin = stack->Parameters.DeviceIoControl.InputBufferLength;
                //得到输出缓冲区大小
                ULONG cout = stack->Parameters.DeviceIoControl.OutputBufferLength;
                //得到IOCTL码
                ULONG CTLcode = stack->Parameters.DeviceIoControl.IoControlCode;
                switch (CTLcode)
                {
                case stringBuffe://缓冲通信模式.
                        KdPrint(("buffer mode\n"));
                        /*获取缓冲区数据,在缓冲交互模式中,系统输入缓冲和系统输出缓冲是同一个,但用户层的缓冲是两个.
                        输入时,从用户输入缓冲复制数据到系统缓冲,操作完IRP之后再从系统缓冲复制数据到用户层的输出缓冲.*/
                        protectBuffer = (CHAR*)Irp->AssociatedIrp.SystemBuffer;
                        KdPrint(("buffer mode view  use string is:%s", protectBuffer));
                    //RtlCopyMemory(protectall, protectBuffer, 384);
                        char *p = NULL, *s=NULL;
                        s = strtok_s(protectBuffer, ",",&p);  //将用户层传进来的多个进程名字符串进行分割
                        if (s!=NULL)
                        {
                                strcpy(protect1, s);  //将分割出来的第一个进程名字复制给内核字符数组
                        }else { strcpy(protect1, "Cheat"); }
                        s = NULL;
                        s = strtok_s(NULL, ",",&p);
                        if (s!=NULL)
                        {
                                strcpy(protect2, s);//将分割出来的第2个进程名字复制给内核字符数组
                        }else {strcpy(protect2, protect1);}
                        
                        s = NULL;
                        s = strtok_s(NULL, ",",&p);
                        if (s!=NULL)
                        {
                                strcpy(protect3, s);//将分割出来的第3个进程名字复制给内核字符数组
                        }else {strcpy(protect3, protect1);}
                        KdPrint(("protect1=%s,protect2=%s,protect3=%s\n", protect1, protect2, protect3));
                        DbgBreakPoint();
                        hookShadowBitBlt();
                        ReadFileToMemory(L"\\??\\C:\\Windows\\System32\\ntkrnlpa.exe", &NewKernalVirualAddress);
                        ReadFileToMemory(L"\\??\\C:\\Windows\\System32\\win32k.sys", &NewWin32kAddress);
                        KdPrint(("new ssdt address is %x\n origkeraddress is%x\n", NewKernalVirualAddress,kerAddress));
                        KdPrint(("new shadowssdt address is %x\n origkeraddress is%x\n", NewWin32kAddress, win32kAddress));
                        RelocationModuleAndBuildSSDT(NewKernalVirualAddress, (PVOID)kerAddress);
                        RelocationModuleAndBuildSSDT(NewWin32kAddress, (PVOID)win32kAddress);
                        buildSSDT(NewKernalVirualAddress, kerAddress);
                        buildShadowSSDT(NewWin32kAddress, kerAddress, win32kAddress);
                        HookKiFastCallEntry();
                        /* inputBuffer = L"from driver",原先我用的这句,其实是个严重的错误,inputBuffer是个指针,
                        此指针的指向的地址是输入输出缓冲,我这样操作的作用是将此指针的指向的地址变成了字符串的地址,而原先的输入输出
                        缓冲依然未被操作,正确的作法如下*/
                        wr = 0;
                        break;
                case stringDirect://直接通信模式
                        KdPrint(("direct mode"));
                        //直接通信模式获取用户层输入的数据与缓冲通信模式相同
                        inputBuffer = (PWCHAR)Irp->AssociatedIrp.SystemBuffer;
                        KdPrint(("direct mode view  use string is %S", inputBuffer));
                        //返回给用户层数据时用的缓冲区就和缓冲通信模式不同了,这种模式是直接叫用户空间输出地址映射进内核.
                        PWCHAR outBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
                        RtlCopyMemory(outBuffer, direct, wcslen(direct) * 2);
                        wr = wcslen(direct) * 2;//设置实际操作的字节数
                        UnHookShadow();//ShadowSSDT的Hook要在这个用户层传递进来的IRP例程中卸载,不能在驱动卸载例程里卸载。
                        break;
                case stringNeither:
                        __try{
                                KdPrint(("neither mode"));
                                //其它通信模式获取用户层输入的数据如下,这种模式是直接取用户空间的缓冲地址,相对不安全.
                                inputBuffer = stack->Parameters.DeviceIoControl.Type3InputBuffer;
                                ProbeForRead(inputBuffer, cin, 4);
                                KdPrint(("neither mode view use string is %S", inputBuffer));
                                //其它通信模式返回用户数据操作如下
                                PWCHAR outBuffer = (PWCHAR)Irp->UserBuffer;
                                ProbeForWrite(outBuffer, cout, 4);//第三个参数是对齐值,第二个参数由上面获得.
                                memcpy(outBuffer, neither, wcslen(neither) * 2 + 2);
                                wr = wcslen(neither) * 2 + 2;
                            }
                                __except(EXCEPTION_EXECUTE_HANDLER)
                                {
                                        KdPrint(("exception for neither"));
                                }

                        break;
                default:
                        break;
                }
        case  IRP_MJ_CLOSE:
                
        default:
                break;
        }
        Irp->IoStatus.Information = wr;//操作了多少字节数,宽字符用wcslen取字节数乘2
        Irp->IoStatus.Status = STATUS_SUCCESS;//返回成功
        IoCompleteRequest(Irp, IO_NO_INCREMENT);//指示完成此IRP
        KdPrint(("leave dispatch function\n"));
        return STATUS_SUCCESS;//返回成功
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
        NTSTATUS status;
        KdPrint(("entry driver registry path is %wZ\n",pRegistryPath));
        pDriverObject->DriverUnload = UnloadDriver;
        status = CreateDevice(pDriverObject);
        pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchMajor;
        //妈的,一定要把这个IRP_MJ_CREATE派遣函数替换成我们自己的,要不然用户层打不开符号链接
        pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchMajor;
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchMajor;
        pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchMajor;
        pDriverObject->MajorFunction[IRP_MJ_WRITE] = DispatchMajor;
        //MyCreateFile();//文件操作
        //HookNtOpenProcess();//SSDTHook
        //DbgBreakPoint();//设试断点
        //inlineHookOpenProcess();NtOpenProcess的inlineHook测试
        kerAddress = EnumDriver(pDriverObject, L"ntoskrnl.exe");//遍历驱动模块.
        win32kAddress = EnumDriver(pDriverObject,L"win32k.sys");
        KeServiceDescriptorTableShadow =(ULONG) &KeServiceDescriptorTable + 0x50;
        KdPrint(("KeServiceDescriptorTable is %x\n KeServiceDescriptorTableshadow is %x", &KeServiceDescriptorTable, KeServiceDescriptorTableShadow));
        return STATUS_SUCCESS;



//最后是用户层通信代码
#include <winioctl.h>//与驱动交互一定要包含此头文件
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define stringBuffe  CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) //缓冲模式交互控制码
#define stringNeither CTL_CODE(FILE_DEVICE_UNKNOWN,0x801,METHOD_NEITHER,FILE_ANY_ACCESS)  //其它模式交互控制码
#define stringDirect CTL_CODE(FILE_DEVICE_UNKNOWN,0x802,METHOD_IN_DIRECT,FILE_ANY_ACCESS)//直接模式交互控制码
HANDLE OpenSymbolic(); 

//打开符号链接
HANDLE OpenSymbolic()
{
        HANDLE hfile= CreateFile(L"\\??\\AssociateDriver", FILE_ALL_ACCESS, 0,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hfile == INVALID_HANDLE_VALUE)
        {
                AfxMessageBox(L"open symbolic failed");
                TRACE("open symbolic failed error code is %x\n", GetLastError());

        }else
        {
                AfxMessageBox(L"open symbolic success");
        }
        return hfile;
}


void CAssociateExeDlg::OnBnClickedBAssocibuffer()
{
        CString outbuffer;
        UpdateData(TRUE);//控件的值刷新到控件变量
        ULONG dwWrite;
        HANDLE HFile = OpenSymbolic();
        memset(outbuffer.GetBuffer(256), 0, 256 * 2);//输出缓冲区清零,否则会出现乱码.
        //宽字符转换多字节集代码开始///
        char *ansiname = new char[384];//为char字符指针分配一块大小512的空间,定义的是指针就得分配内存.
        memset(ansiname, 0, sizeof(char)* 384); //初始化新分配的内存空间,将其全部填充0
        WideCharToMultiByte(CP_ACP, 0, View.GetBuffer(), -1, ansiname, 384, NULL, NULL);
        //宽字符转换多字节集代码结束,就三行///
        DeviceIoControl(HFile, stringBuffe, ansiname, 384, outbuffer.GetBuffer(256), 256 * 2 + 2, &dwWrite, NULL);



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
本实例由VS2008开发,在提供了一套驱动开发框架的同时,又演示了如何获取Shadow SSDT函数原始地址的办法。 主要函数:ULONG GetShadowSSDT_Function_OriAddr(ULONG index); 原理说明: 根据特征码搜索导出函数KeAddSystemServiceTable来获取Shadow SSDT基址,以及通过ZwQuerySystemInformation()函数获取win32k.sys基址,然后解析PE定位到Shadow SSDT在win32k.sys的偏移地址,并通过进一步计算来得到Shadow SSDT函数的原始地址。 这里只测试了三个函数:(460)NtUserMessageCall、(475)NtUserPostMessage和(502)NtUserSendInput,具体使用时可以举一反三,网上完整的源代码实例并不太多,希望可以帮到真正有需要的朋友。 系统环境: 在WinXP SP3系统 + 瑞星杀毒软件 打印输出: [ LemonInfo : Loading Shadow SSDT Original Address Driver... ] [ LemonInfo : 创建“设备”值为:0 ] [ LemonInfo : 创建“设备”成功... ] [ LemonInfo : 创建“符号链接”状态值为:0 ] [ LemonInfo : 创建“符号链接”成功... ] [ LemonInfo : 驱动加载成功... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 开始... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP Enter IRP_MJ_DEVICE_CONTROL... ] [ LemonInfo : 获取ShadowSSDT (460)NtUserMessageCall 函数的“当前地址”为:0xB83ECFC4,“起源地址”为:0xBF80EE6B ] [ LemonInfo : 获取ShadowSSDT (475)NtUserPostMessage 函数的“当前地址”为:0xB83ECFA3,“起源地址”为:0xBF8089B4 ] [ LemonInfo : 获取ShadowSSDT (502)NtUserSendInput 函数的“当前地址”为:0xBF8C31E7,“起源地址”为:0xBF8C31E7 ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP_MJ_DEVICE_CONTROL 成功执行... ] [ LemonInfo : 派遣函数(DispatchRoutine) IRP 结束... ] [ LemonInfo : UnLoading Shadow SSDT Original Address Driver... ] [ LemonInfo : 删除“符号链接”成功... ] [ LemonInfo : 删除“设备”成功... ] [ LemonInfo : 驱动卸载成功... ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值