学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

7

积分

0

好友

1

主题
发表于 前天 11:55 | 查看: 22| 回复: 2
1.什么是ETWHOOK?
简单的说就是在有PatchGurad的windows系统上SSDT hook的一种替代方案



2.ETWHOOK实现步骤
(1).替换HalpPrivatDispatchTable(可以通过MmGetSystemRoutineAddress函数获取)表中的的EtwpReserveWithPmcCounters函数指针为自己的代理函数,代理函数的功能是在堆栈中寻找系统调用的目标内核函数指针并且在找到的时候替换它;
(2).开启Windows的ETW功能,并且经过一系列的设置使得系统调用时能进入EtwpReserveWithPmcCounters函数(经过第一步的操作之后EtwpReserveWithPmcCounters已经被替换成自己的代理函数了);


3.代码
(1).HalColleePmcCounts代理函数的实现:
[pre]VOID ProxyEtwpReserveWithPmcCounters(PVOID Context, ULONGLONG TraceBuff)
{


        ULONG Signate = SYSCALL_SIGNATE;
        ULONG Magic = SYSCALL_MAGIC;
        //PULONG64 RspPos = _AddressOfReturnAddress();
        //PULONG64 RspLimit = __readgsqword(0x1a8);
        LARGE_INTEGER* RspPos = _AddressOfReturnAddress();
        LARGE_INTEGER* RspLimit = __readgsqword(0x1a8);
        ULONG64 KiSystemServiceRepeat = HookInfo->KiSystemServiceRepeat;
       
        if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
        {
                if (ExGetPreviousMode() == KernelMode)return;
                if (!KiSystemServiceRepeat)
                {
                        DbgPrint("failed to find KiSystemServiceRepeat\r\n");
                        return;
                }
                while (RspPos <= RspLimit)
                {
                        //if (*((PUSHORT)(RspPos)) == Magic)
                        if((RspPos->LowPart&0x0000ffff)== Signate)
                        {
                               
                                if (RspPos[1].LowPart == Magic)
                                {
                                               
                                        for (; (ULONG64)RspPos <= (ULONG64)RspLimit; ++RspPos)
                                        {
                                               
                                                if (RspPos->QuadPart >= (KiSystemServiceRepeat & 0XFFFFFFFFFFFFF000) &&
                                                        RspPos->QuadPart <= ((KiSystemServiceRepeat & 0XFFFFFFFFFFFFF000) + 0x2000))
                                                {
                                                       
                                                        //TargetFind!
                                                       
                                                        if (RspPos[9].QuadPart == HookInfo->OrgFunc)
                                                        {
                                                                HookInfo->Lock = FALSE;
                                                                KiSystemServiceRepeat = HookInfo->HookFunc;
                                                                InterlockedExchange64(&(RspPos[9].QuadPart), KiSystemServiceRepeat);
                                                        }
                                                        goto sub_1;
                                                }
                                        }
                                }
                               
                        }
                        ++RspPos;
                       
                }

        }
sub_1:

        return HookInfo->OrigeHalCollectPmcCounters(Context, TraceBuff);
}[/pre](2).最麻烦的是开启ETW功能以及开启ETW子功能PMC计数
[pre]//开启ETW子功能PMC计数需要获取MaxPmcCounter
UCHAR* GetEtwpMaxPmcCounter()
{
        UCHAR* Ret = 0;
        LONG64 KernelBase = 0;
        UNICODE_STRING KernelModuleName;
        UCHAR FeaturesCode[] = { 0x44,0x3b,0x05,'*','*','*','*',0x0f,0x87,'*','*','*','*',0x83,0xb9,'*','*','*','*',0x01,0x0f,0x84,'*','*','*','*',0x48,0x83,0xb9,'*','*','*','*',0x00,0x75,'*' };
        UCHAR SectionNames[] = { 'P','A','G','E',0,0,0,0 };
        RtlInitUnicodeString(&KernelModuleName, L"ntoskrnl.exe");
        KernelBase = HookInfo->OsKernelLoadBase;
        if (!KernelBase)
        {
                DbgPrint("get kernel module base fail\n");
                return 0;
        }
        IMAGE_FILE_HEADER* FileHeader = KernelBase + ((IMAGE_DOS_HEADER*)KernelBase)->e_lfanew + 4;
        USHORT SectionCount = FileHeader->NumberOfSections;
        IMAGE_OPTIONAL_HEADER64* OptionalHeader = FileHeader + 1;
        IMAGE_SECTION_HEADER* SectionHeader = OptionalHeader + 1;
        LONG64 StratAdd = 0;
        LONG64 EndAdd = 0;
        for (int i = 0; i < SectionCount; ++i)
        {
               
                if (RtlCompareMemory(&(SectionHeader.Name), SectionNames, 8) == 8)
                {
                        StratAdd = SectionHeader.VirtualAddress + KernelBase;
                        EndAdd = ((LONG64)(SectionHeader.Misc.VirtualSize & 0xfffff000)) + 0x1000 + StratAdd;
                       
                        if (SearchFunctionOfCharacteristicCode(FeaturesCode,
                                36,
                                StratAdd,
                                EndAdd,
                                &Ret) == STATUS_SUCCESS)
                        {
                               
                                return (*((PULONG)(Ret+3)))+((LONG64)Ret+7);
                        }
                        break;
                }
        }
        return 0;

}


//开启ETW功能分支功能PerformatsCounts计数功能
NTSTATUS SetPmcCounts()
{
        NTSTATUS state = STATUS_UNSUCCESSFUL;
        if (!HookInfo->EtwOffNo)
        {
                return state;
        }
        PETW_PMC_INFO PmcInfo = ExAllocatePool(PagedPool, 0X1000);
        if (!PmcInfo)
        {
                DbgPrint("Allocate PmcInfo fail!\n");
                return state;
        }
       
        PmcInfo->EventTraceInformationClass = EventTraceProfileCounterListInformation;
        PmcInfo->TraceHandle = 2;
        PmcInfo->ProfileSource[0] = 1;
        UCHAR* EtwPmcMaxCount = GetEtwpMaxPmcCounter();
        UCHAR* OrgMaxCount = 0;
        if (EtwPmcMaxCount)
        {
                OrgMaxCount = *EtwPmcMaxCount;
                if (OrgMaxCount <= 1)*EtwPmcMaxCount = 2;
        }
        state = ZwSetSystemInformation(SystemPerformanceTraceInformation, PmcInfo, sizeof(ETW_PMC_INFO));
        if (!NT_SUCCESS(state))
        {
                DbgPrint("pmc open fail\n");
                return state;
        }
        if (EtwPmcMaxCount)
        {
                if (OrgMaxCount <= 1)*EtwPmcMaxCount = OrgMaxCount;
        }
        PETW_SYSTEM_TRACE SysEventTraceInfo = ExAllocatePool(PagedPool, 0x1000);
        if (!SysEventTraceInfo)
        {
                DbgPrint("allocate System Event Trace info fail\r\n");
                return STATUS_UNSUCCESSFUL;
        }
        SysEventTraceInfo->EventTraceInformationClass = EventTraceProfileEventListInformation;
        SysEventTraceInfo->TraceHandle = 2;
        //SysEventTraceInfo->HookId[0] = HookInfo->SyscallHookId;
        SysEventTraceInfo->HookId[0] = SYSCALL_SIGNATE;

        state = ZwSetSystemInformation(SystemPerformanceTraceInformation, SysEventTraceInfo, sizeof(ETW_SYSTEM_TRACE));
        if (!NT_SUCCESS(state))
        {
                DbgPrint("failed to configure pmc event,errcode=%x\r\n", state);
                return state;
        }
        if (SysEventTraceInfo)ExFreePool(SysEventTraceInfo);
        if (PmcInfo)ExFreePool(PmcInfo);
        return state;
}

//关闭ETW事件跟踪功能
NTSTATUS EndEventTraceWork()
{

        NTSTATUS state = STATUS_UNSUCCESSFUL;
        ULONG LenthRetVaul=0;
        EVENT_TRACE_PROPERTIES* EtwInfo = ExAllocatePool(PagedPool, 0x1000);
        if (!EtwInfo)
        {
                DbgPrint("2_AllocateMemFial!\n");
        }
        memset(EtwInfo, 0, 0x1000);
        EtwInfo->Wnode.BufferSize = 0x1000;
        EtwInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
        RtlInitUnicodeString(&(EtwInfo->ProviderName), L"Circular Kernel Context Logger");
        EtwInfo->Wnode.Guid = CkclSessionGuid;
        EtwInfo->Wnode.ClientContext = 1;
        EtwInfo->BufferSize = sizeof(ULONG);
        EtwInfo->MinimumBuffers = 2;
        EtwInfo->MaximumBuffers = 2;
        EtwInfo->LogFileMode = EVENT_TRACE_BUFFERING_MODE;

        state = ZwTraceControl(EtwpStopTrace, EtwInfo, 0x1000, EtwInfo, 0x1000, &LenthRetVaul);
        if (!NT_SUCCESS(state))
        {
                DbgPrint("stop event trace fail!-state=%x\n", state);
        }
        if (EtwInfo)ExFreePool(EtwInfo);
        HookInfo->EtwOffNo = FALSE;
        return state;

}

//开启ETW事件跟踪功能
NTSTATUS StartEventTraceWork()
{
        NTSTATUS state=STATUS_UNSUCCESSFUL;
        ULONG LenthRetVaul=0;
        EVENT_TRACE_PROPERTIES* EtwInfo = ExAllocatePool(PagedPool, 0x1000);
        if (!EtwInfo)
        {
                DbgPrint("1_AllocateMemFial!\n");
        }
        memset(EtwInfo, 0, 0x1000);
       
        EtwInfo->Wnode.BufferSize = 0x1000;
        EtwInfo->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
        RtlInitUnicodeString(&(EtwInfo->ProviderName), L"Circular Kernel Context Logger");
        EtwInfo->Wnode.Guid = CkclSessionGuid;
        EtwInfo->Wnode.ClientContext = 1;
        EtwInfo->BufferSize = sizeof(ULONG);
        EtwInfo->MinimumBuffers = 2;
        EtwInfo->MaximumBuffers = 2;
        EtwInfo->LogFileMode = EVENT_TRACE_BUFFERING_MODE;
        state = ZwTraceControl(EtwpStartTrace, EtwInfo, 0X1000, EtwInfo,0x1000, &LenthRetVaul);
        if (!NT_SUCCESS(state) && state != STATUS_OBJECT_NAME_COLLISION)
        {
                DbgPrint("EventTrace Start Fail-state=%x\n",state);
        }
        EtwInfo->EnableFlags = EVENT_TRACE_FLAG_SYSTEMCALL;

        state = ZwTraceControl(EtwpUpdateTrace, EtwInfo, 0x1000, EtwInfo, 0x1000, &LenthRetVaul);
        if (!NT_SUCCESS(state))
        {
                DbgPrint("failed to enable syscall etw-state=%x\n", state);
        }
        if (EtwInfo)
        {
                ExFreePool(EtwInfo);
        }
        return state;
}
[/pre]
4.具体原理
WINDOWS的.data节一般是存放可以变的全局变量的,windows不可避免的要使用全局变量存放一些比较底层函数指针,所谓的偷指针就是修改.data节区的函数指针。
而在这里,最好玩的是HalPrivateDispatchTable,这个是windows 的ntoskenl.exe为了方便使用HAL的导出函数,把他们存放在统一的地方。而HAL,用到的地方肯定很多,ETW 正是如此。
看一下系统调用ETW的调用路径。
这里其实可以看到,call rax 其实就是正常的系统调用,而再进入ETW系统调用之前,他把原始的系统调用存放在了栈上,这就导致我们拦截到ETW的时候,可以修改栈上的位置,来进行HOOK Syscall。

ETW hook原理与C代码实现,附完整源代码

ETW hook原理与C代码实现,附完整源代码
如果说之前无法定位,现在可以通过这个栈上面的magic number来定位系统调用的目标函数的地址,从而替换了。
继续跟到EtwTraceSiloKernelEvent里面,可以发现,无论参数怎么样,这个函数调用了EtwpLogKernelEvent。继续跟踪EtwpLogKernelEvent会发现EtwpLogKernelEvent会调用EtwpReserveWithPmcCounters。
而EtwpReserveWithPmcCounters就是这次事件的主角,关键部分代码为:

ETW hook原理与C代码实现,附完整源代码

ETW hook原理与C代码实现,附完整源代码
图中红圈中的地方就是EtwpReserveWithPmcCounters通过CFG保护的方式调用HalPrivateDispatchTable表中的HalCollectPmcCounters。
我们替换HalPrivateDispatchTable表中的EtwpReserveWithPmcCounters函数指针为我们自己的函数,在我们自己的函数里就可以在栈上查找并替换系统调用的目标函数从而达到HOOk的目的。
最后附上完整源代码
游客,如果您要查看本帖隐藏内容请回复






温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
发表于 前天 17:25
不错

    发表于 昨天 15:08
    感谢分享,我会认真学习的!

    小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

    GMT+8, 2025-1-9 03:37 , Processed in 0.130796 second(s), 49 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表