首页
社区
课程
招聘
[原创]调试陷阱ThreadHideFromDebugger的另一种对抗方法
发表于: 2019-2-27 10:55 18763

[原创]调试陷阱ThreadHideFromDebugger的另一种对抗方法

2019-2-27 10:55
18763
测试环境: windows 7 x64
炒个冷饭,说说ThreadHideFromDebugger的另一种对抗方法,之所以说是另一种是因为第一种方法 无论是r3还是r0都可以用hook解决。
接下来说说另一种方法 ,先来看一个函数定义:
NTSTATUS
NTAPI
NtSetInformationThread(
    _In_ HANDLE ThreadHandle,
    _In_ THREADINFOCLASS ThreadInformationClass,
    _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation,
    _In_ ULONG ThreadInformationLength
    );
它是这么用的:
NtSetInformationThread(handle.get(), ThreadHideFromDebugger, nullptr, 0);

NTSTATUS
NTAPI
NtSetInformationThread(
    _In_ HANDLE ThreadHandle,
    _In_ THREADINFOCLASS ThreadInformationClass,
    _In_reads_bytes_(ThreadInformationLength) PVOID ThreadInformation,
    _In_ ULONG ThreadInformationLength
    );
它是这么用的:
NtSetInformationThread(handle.get(), ThreadHideFromDebugger, nullptr, 0);

NtSetInformationThread(handle.get(), ThreadHideFromDebugger, nullptr, 0);

再来看一个结构定义:
typedef enum _THREADINFOCLASS {
    ThreadBasicInformation          = 0,
    ThreadTimes                     = 1,
    ThreadPriority                  = 2,
    ThreadBasePriority              = 3,
    ThreadAffinityMask              = 4,
    ThreadImpersonationToken        = 5,
    ThreadDescriptorTableEntry      = 6,
    ThreadEnableAlignmentFaultFixup = 7,
    ThreadEventPair_Reusable        = 8,
    ThreadQuerySetWin32StartAddress = 9,
    ThreadZeroTlsCell               = 10,
    ThreadPerformanceCount          = 11,
    ThreadAmILastThread             = 12,
    ThreadIdealProcessor            = 13,
    ThreadPriorityBoost             = 14,
    ThreadSetTlsArrayAddress        = 15,   // Obsolete
    ThreadIsIoPending               = 16,
    ThreadHideFromDebugger          = 17,
    ThreadBreakOnTermination        = 18,
    ThreadSwitchLegacyState         = 19,
    ThreadIsTerminated              = 20,
    ThreadLastSystemCall            = 21,
    ThreadIoPriority                = 22,
    ThreadCycleTime                 = 23,
    ThreadPagePriority              = 24,
    ThreadActualBasePriority        = 25,
    ThreadTebInformation            = 26,
    ThreadCSwitchMon                = 27,   // Obsolete
    ThreadCSwitchPmu                = 28,
    ThreadWow64Context              = 29,
    ThreadGroupInformation          = 30,
    ThreadUmsInformation            = 31,   // UMS
    ThreadCounterProfiling          = 32,
    ThreadIdealProcessorEx          = 33,
    ThreadCpuAccountingInformation  = 34,
    ThreadSuspendCount              = 35,
    ThreadActualGroupAffinity       = 41,
    ThreadDynamicCodePolicyInfo     = 42,
    ThreadSubsystemInformation      = 45,

    MaxThreadInfoClass              = 51,
} THREADINFOCLASS;

typedef enum _THREADINFOCLASS {
    ThreadBasicInformation          = 0,
    ThreadTimes                     = 1,
    ThreadPriority                  = 2,
    ThreadBasePriority              = 3,
    ThreadAffinityMask              = 4,
    ThreadImpersonationToken        = 5,
    ThreadDescriptorTableEntry      = 6,
    ThreadEnableAlignmentFaultFixup = 7,
    ThreadEventPair_Reusable        = 8,
    ThreadQuerySetWin32StartAddress = 9,
    ThreadZeroTlsCell               = 10,
    ThreadPerformanceCount          = 11,
    ThreadAmILastThread             = 12,
    ThreadIdealProcessor            = 13,
    ThreadPriorityBoost             = 14,
    ThreadSetTlsArrayAddress        = 15,   // Obsolete
    ThreadIsIoPending               = 16,
    ThreadHideFromDebugger          = 17,
    ThreadBreakOnTermination        = 18,
    ThreadSwitchLegacyState         = 19,
    ThreadIsTerminated              = 20,
    ThreadLastSystemCall            = 21,
    ThreadIoPriority                = 22,
    ThreadCycleTime                 = 23,
    ThreadPagePriority              = 24,
    ThreadActualBasePriority        = 25,
    ThreadTebInformation            = 26,
    ThreadCSwitchMon                = 27,   // Obsolete
    ThreadCSwitchPmu                = 28,
    ThreadWow64Context              = 29,
    ThreadGroupInformation          = 30,
    ThreadUmsInformation            = 31,   // UMS
    ThreadCounterProfiling          = 32,
    ThreadIdealProcessorEx          = 33,
    ThreadCpuAccountingInformation  = 34,
    ThreadSuspendCount              = 35,
    ThreadActualGroupAffinity       = 41,
    ThreadDynamicCodePolicyInfo     = 42,
    ThreadSubsystemInformation      = 45,

    MaxThreadInfoClass              = 51,
} THREADINFOCLASS;

好,那我们看下对于这个参数,NtSetInformationThread是怎么实现的:
    case ThreadHideFromDebugger:
        if (ThreadInformationLength != 0) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HIDEFROMDBG);

        ObDereferenceObject (Thread);

        return st;
        break;
接下来,来看看调试子系统对于有“PS_CROSS_THREAD_FLAGS_HIDEFROMDBG”这个标志的线程是怎么处理的:
    case ThreadHideFromDebugger:
        if (ThreadInformationLength != 0) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ThreadHandle,
                                        THREAD_SET_INFORMATION,
                                        PsThreadType,
                                        PreviousMode,
                                        &Thread,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        PS_SET_BITS (&Thread->CrossThreadFlags, PS_CROSS_THREAD_FLAGS_HIDEFROMDBG);

        ObDereferenceObject (Thread);

        return st;
        break;
接下来,来看看调试子系统对于有“PS_CROSS_THREAD_FLAGS_HIDEFROMDBG”这个标志的线程是怎么处理的:
    Process = PsGetCurrentProcess();
    if (DebugException) {
        if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
            Port = NULL;
        } else {
            Port = Process->DebugPort;
        }
        LpcPort = FALSE;
    } else {
        Port = Process->ExceptionPort;
        m.h.u2.ZeroInit = LPC_EXCEPTION;
        LpcPort = TRUE;
    }
emmm...破案了,原来清空DebugPort不是某P首创,人家巨硬的内核里本来就是这么做的,只不过一个是暴力清空而另一个是“假装没有”。
    Process = PsGetCurrentProcess();
    if (DebugException) {
        if (PsGetCurrentThread()->CrossThreadFlags&PS_CROSS_THREAD_FLAGS_HIDEFROMDBG) {
            Port = NULL;
        } else {
            Port = Process->DebugPort;
        }
        LpcPort = FALSE;
    } else {
        Port = Process->ExceptionPort;
        m.h.u2.ZeroInit = LPC_EXCEPTION;
        LpcPort = TRUE;
    }
emmm...破案了,原来清空DebugPort不是某P首创,人家巨硬的内核里本来就是这么做的,只不过一个是暴力清空而另一个是“假装没有”。
这也就非常符合微软对“ ThreadHideFromDebugger ”这个参数的描述:设置此参数将使这条线程对调试器“隐藏”。即调试器收不到调试信息,因而就会出现当调试器对被调试进程下断点,线程执行到被下断点代码的时候就会卡死这种现象。
OK,接下来我们考虑一下怎么干掉它。
看以下定义:
#define RtlInterlockedSetBitsDiscardReturn(Flags, Flag) \
    (VOID) RtlInterlockedSetBits(Flags, Flag)

#define RtlInterlockedSetBitsDiscardReturn(Flags, Flag) \
    (VOID) RtlInterlockedSetBits(Flags, Flag)

#define PS_SET_BITS(Flags, Flag) \
    RtlInterlockedSetBitsDiscardReturn (Flags, Flag)
还记得这句代码吗?
#define PS_SET_BITS(Flags, Flag) \
    RtlInterlockedSetBitsDiscardReturn (Flags, Flag)

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-2-27 23:49 被黑洛编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (20)
雪    币: 1535
活跃值: (695)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
2019-2-27 13:53
0
雪    币: 1140
活跃值: (102)
能力值: ( LV4,RANK:48 )
在线值:
发帖
回帖
粉丝
3
2019-2-27 16:16
0
雪    币: 368
活跃值: (441)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
4
要是优雅的干掉Hidefromdebugger
需要重写掉调试流程的某些函数,干脆不用这个标志位就好了
3环枚举符号发送到0环,0环对着ida和wrk的源代码抄,论坛也有类似的代码
自己创建新的DebugPort类型,顺便也过了句柄表检测

最后于 2019-3-5 21:55 被又出bug了编辑 ,原因:
2019-3-5 21:54
0
雪    币: 30
活跃值: (905)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
又出bug了 要是优雅的干掉Hidefromdebugger需要重写掉调试流程的某些函数,干脆不用这个标志位就好了3环枚举符号发送到0环,0环对着ida和wrk的源代码抄,论坛也有类似的代码自己创建新的DebugP ...
求教win7 和win10 如何新建DebugPort
2019-3-5 21:59
0
雪    币: 368
活跃值: (441)
能力值: ( LV2,RANK:140 )
在线值:
发帖
回帖
粉丝
6
uvbs 求教win7 和win10 如何新建DebugPort
DbgkInitialize (
    VOID
    )
/*++

Routine Description:

    Initialize the debug system

Arguments:

    None

Return Value:

    NTSTATUS - Status of operation

--*/
{
    NTSTATUS Status;
    UNICODE_STRING Name;
    OBJECT_TYPE_INITIALIZER oti = {0};
    GENERIC_MAPPING GenericMapping = {STANDARD_RIGHTS_READ | DEBUG_READ_EVENT,
                                      STANDARD_RIGHTS_WRITE | DEBUG_PROCESS_ASSIGN,
                                      STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
                                      DEBUG_ALL_ACCESS};


    PAGED_CODE ();

    ExInitializeFastMutex (&DbgkpProcessDebugPortMutex);

    RtlInitUnicodeString (&Name, L"DebugObject");

    oti.Length                    = sizeof (oti);
    oti.SecurityRequired          = TRUE;
    oti.InvalidAttributes         = 0;
    oti.PoolType                  = NonPagedPool;
    oti.DeleteProcedure           = DbgkpDeleteObject;
    oti.CloseProcedure            = DbgkpCloseObject;
    oti.ValidAccessMask           = DEBUG_ALL_ACCESS;
    oti.GenericMapping            = GenericMapping;
    oti.DefaultPagedPoolCharge    = 0;
    oti.DefaultNonPagedPoolCharge = 0;

    Status = ObCreateObjectType (&Name, &oti, NULL, &DbgkDebugObjectType);
    if (!NT_SUCCESS (Status)) {
        return Status;
    }
    return Status;
}
--
直接抄,wrk提供了源代码,把其余函数的符号位置找到就好了,这个是楼主的帖子,写多不好(win7和win10也有这个函数,你打开ida逆下基本没怎么改)
最后于 2019-3-5 22:05 被又出bug了编辑 ,原因:
2019-3-5 22:04
0
雪    币: 6124
活跃值: (4721)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
7
又出bug了 uvbs 求教win7 和win10 如何新建DebugPort DbgkInitialize ( &nbs ...
实际上,你说的这个方法是最好的,也是能根本解决问题的。还准备啥时候再写写骗个回复什么的。
2019-3-6 00:59
0
雪    币: 1385
活跃值: (376)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这饭好冷
2019-3-8 16:53
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
黑洛 实际上,你说的这个方法是最好的,也是能根本解决问题的。还准备啥时候再写写骗个回复什么的。
挖坟了,不明白为什么我SSDT HOOK了 NtSetInformationThread 并没有收到第二个参数为0x11的这条记录,大多数参数值是0x5。

我在应用层已经调用了ZwSetInformationThread ,SSDT  Hook 用的是论坛找的一位老哥的 接管MSR那份代码,系统是Win7 64位。

尝试了很多次,无论是自己写的程序调用ZwSetInformationThread,还是某游戏(调试器无法收到消息,我怀疑调用了该函数,使用CE的VEH能捕获到中断信息,使用其他方式捕获断点都会因为没有程序来处理这个异常而导致游戏崩溃),最奇怪的还是NtSetInformationThread接收不到我自己程序调用ZwSetInformationThread的过程,但是ZwSetInformationThread确实生效了。
2019-8-5 15:51
0
雪    币: 6124
活跃值: (4721)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
10
Heiyiren123 挖坟了,不明白为什么我SSDT HOOK了 NtSetInformationThread 并没有收到第二个参数为0x11的这条记录,大多数参数值是0x5。 我在应用层已经调用了ZwSetInfo ...
这个问题,你要看KiUserExceptionDispatcher是不是被hook了,比如某P,或者在+8处(11平台)
2019-8-5 18:19
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
黑洛 这个问题,你要看KiUserExceptionDispatcher是不是被hook了,比如某P,或者在+8处(11平台)
 首先感谢老哥抽空回复我。

我是用XT检测看到游戏在R3层没有任何HOOK,并且R0层没有任何驱动,且我自己写的程序调用ZwSetInformationThread 函数,在R0层的HOOK中也无法收到任何消息,系统调用NtSetInformationThread倒是能被我拦截到几个。

不知道是不是因为MSR接管的只有KiFastCallEntry进入内核的函数调用,而通过中断门调用的KiSystemServeive并没有被我所拦截。

目前基础还比较差,自己动手实现验证所想暂时还做不到,还在阅读内核相关材料,所以想问问老哥这个思路对不对,思路对了我后面把PG补丁给打了,然后直接 HOOK 就可以了。
2019-8-6 14:19
0
雪    币: 9626
活跃值: (1838)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
12
Heiyiren123 [em_76] 首先感谢老哥抽空回复我。 我是用XT检测看到游戏在R3层没有任何HOOK,并且R0层没有任何驱动,且我自己写的程序调用ZwSetInformationThread 函数,在R0层 ...
听你这么一说还没打pg补丁,那现在应该还是在虚拟机测试咯。
自己调用这个api是在有游戏的环境下还是没游戏的环境下?
有游戏的环境下多半是检测有操作,没游戏的话检查自己的代码。
最后于 2019-8-6 15:06 被Sprite雪碧编辑 ,原因:
2019-8-6 15:05
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
Sprite雪碧 Heiyiren123 [em_76] 首先感谢老哥抽空回复我。 我是用XT检测看到游戏在R3层没有任何HOOK,并且R0层没有任何驱动,且我自己写的程 ...
我使用的是一套接管MSR进行SSDT的代码,确实能捕获到其他调用的信息,比如系统某些其他函数调用了NtSetInformationThread,我都能捕获到并且打印出来,奇怪的是我自己的代码直接调用的ZwSetInformationThread消息没有被拦截到,我回去再检查检查,也再试试把PG干掉之后直接Hook看看。

目前的情况总结下就是这样:

1.在虚拟机里面,Win7 64位,没打PG补丁,用的过期驱动签名。
2.是通过接管MSR来实现SSDT的HOOK,能拦截到一些R3层调用API,但是HOOK了NtSetInformationThread,部分调用拦截不到(还是可以收到一些调用消息,比如第二个参数值位5和3的调用),怀疑接管MSR这种方式有问题,但不能确定下来是什么问题。
3.因为之前学习的时候学到3环进入0环有2种方式,systementry 和 中断门,而且systementry是走的KiFastCallEntry这条路线,但印象中那套接管MSR的代码是HOOK KiFastCallEntry入口来实现的,所以怀疑自己是不是漏掉了通过中断门这种方式进入R0层的代码。

  还有就是多谢老哥们的指点,待我解决这个问题之后,会把最后的解决方法发在这下面。
2019-8-6 18:59
0
雪    币: 300
活跃值: (2532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
mark
2019-8-6 20:24
0
雪    币: 9626
活跃值: (1838)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
15
Heiyiren123 我使用的是一套接管MSR进行SSDT的代码,确实能捕获到其他调用的信息,比如系统某些其他函数调用了NtSetInformationThread,我都能捕获到并且打印出来,奇怪的是我自己的代码直接调用的 ...
int2e好像是在系统启动期间的时候走的,之后好像就一直是syscall,不会走int2e。
可以尝试替换函数指针来进行hook,不一定非要接管msr。
2019-8-6 21:19
0
雪    币: 6124
活跃值: (4721)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
16
Heiyiren123 我使用的是一套接管MSR进行SSDT的代码,确实能捕获到其他调用的信息,比如系统某些其他函数调用了NtSetInformationThread,我都能捕获到并且打印出来,奇怪的是我自己的代码直接调用的 ...
具体是什么游戏?毕竟涉及到游戏了,如果有经验的话还是不需要去盲猜了。
2019-8-6 22:44
0
雪    币: 4793
活跃值: (4484)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
我凑,不知道该说什么。。留个足迹。。。
2019-8-7 03:57
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
黑洛 具体是什么游戏?毕竟涉及到游戏了,如果有经验的话还是不需要去盲猜了。
我在研究调试魔域这款游戏,他驱动没加载起来,只有R3层的反调试,CE的 VEH 模式下断点都没问题,但是调试器收不到消息。

注:因为自己以前玩过这款游戏,所以拿这款游戏做研究,学习他的反调试系统,不是想做外挂。
2019-8-9 16:39
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
大佬加一下我的qq1255165501 高价请教您几个问题 自己搞不懂
2020-3-7 16:07
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
接下来,来看看调试子系统对于有“PS_CROSS_THREAD_FLAGS_HIDEFROMDBG”这个标志的线程是怎么处理的

这个处理函数叫啥?
2022-12-9 05:26
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
又出bug了 要是优雅的干掉Hidefromdebugger需要重写掉调试流程的某些函数,干脆不用这个标志位就好了3环枚举符号发送到0环,0环对着ida和wrk的源代码抄,论坛也有类似的代码自己创建新的DebugP ...
debugport 不是 debugobject ...
2022-12-9 05:53
0
游客
登录 | 注册 方可回帖
返回
//