吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3919|回复: 50
上一主题 下一主题
收起左侧

[原创] [提前庆祝:除夕礼物]r3反反调试插件hook漏洞利用

  [复制链接]
跳转到指定楼层
楼主
成熟的美羊羊 发表于 2023-1-19 23:07 回帖奖励
本帖最后由 成熟的美羊羊 于 2023-1-20 01:21 编辑

r3反反调试插件漏洞利用

本帖内容:ThreadHideFromDebugger也能检测调试器
附件上传为 r3反调试.zip

注意

由于电脑坏了,所以本帖内容包括代码皆为手机编写,可能含有大量失误。
感谢群友oiGzewo与yeyue的测试。

参考链接

alkhaser
ScyllaHIDE
一个反调试笔记

简写

NtSetInfomationTheead set
NtQueryInfomationTheead query

看看反调试是如何实现的

例一

作者只是简单的获取了set的地址并调用set,没有任何的校验,这样的反调试是十分弱小的。

// 反调试函数
void HideFromDebugger(){
    HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
    NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hNtDll,"NtSetInformationThread");
    NTSTATUS status = NtSetInformationThread(GetCurrentThread(),ThreadHideFromDebugger,NULL,0);
}

例二

作者使用了set和query,并对一些反反调试可能存在的问题进行了测试。
但是query操作并未对ThreadHideFromDebugger标志位进行校验导致其能被sharp0d与ScyllaH1DE等插件anti。

auto NtSetInformationThread = static_cast<pNtSetInformationThread>(API::GetAPI(API_IDENTIFIER::API_NtSetInformationThread));
    auto NtQueryInformationThread = static_cast<pNtQueryInformationThread>(API::GetAPI(API_IDENTIFIER::API_NtQueryInformationThread));

    NTSTATUS Status;
    bool doQITcheck = false;

    // only do the QueryInformationThread check if we're on Vista and the API is available.
    // this is because the ThreadHideFromDebugger class can only be queried from Vista onwards.
    if (API::IsAvailable(API_IDENTIFIER::API_NtQueryInformationThread))
    {
        doQITcheck = IsWindowsVistaOrGreater();
    }

    AlignedBool isThreadHidden;
    isThreadHidden.Value = false;

    // First issue a bogus call with an incorrect length parameter. If it succeeds, we know NtSetInformationThread was hooked.
    Status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &isThreadHidden, 12345);
    if (Status == 0)
        return TRUE;

    // Next try again but give it a bogus thread handle. If it succeeds, again we know NtSetInformationThread was hooked.
    Status = NtSetInformationThread((HANDLE)0xFFFF, ThreadHideFromDebugger, NULL, 0);
    if (Status == 0)
        return TRUE;

    // Now try a legitimate call.
    Status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);

    if (Status == 0)
    {
        if (doQITcheck)
        {
            // note: the ThreadHideFromDebugger query expects a bool (1 byte), not a BOOL (4 bytes)
            // if a BOOL is used, the kernel returns 0xC0000004 (STATUS_INFO_LENGTH_MISMATCH) because BOOL is typedef int.

            // first do a legitimate call. this should succeed or return an error such as access denied.
            Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &isThreadHidden.Value, sizeof(bool), NULL);

            // this shouldn't happen, because we used the correct length. this will only happen if a buggy hook mistakenly expects a BOOL rather than a bool.
            if (Status == STATUS_INFO_LENGTH_MISMATCH)
            {
                // we found a buggy hook that expects some size other than 1
                return TRUE;
            }

            // if the legitimate call succeeded, continue with additional bogus API call checks
            if (Status == 0)
            {
                AlignedBool bogusIsThreadHidden;
                bogusIsThreadHidden.Value = false;

                // now do a bogus call with the wrong size. this will catch buggy hooks that accept BOOL (4 bytes) or just don't have any size checks
                Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &bogusIsThreadHidden.Value, sizeof(BOOL), NULL);
                if (Status != STATUS_INFO_LENGTH_MISMATCH)
                {
                    // we found a buggy hook that allows for incorrect size values
                    return TRUE;
                }

                // NtQueryInformationThread explicitly requires the ThreadInformation pointer to be aligned. as such, it should reject unaligned pointers.
                // hooks are almost certainly guaranteed to not retain this behaviour, so it's a very nice way to catch them out.
                const size_t UnalignedCheckCount = 8;
                bool bogusUnalignedValues[UnalignedCheckCount];
                int alignmentErrorCount = 0;
#if _WIN64
                // on 64-bit, up to two elements in the array should be aligned.
                const size_t MaxAlignmentCheckSuccessCount = 2;
#else
                // on 32-bit, there should be either two or four aligned elements (unsure how WoW64 affects this, so I'm just gonna assume 2 or 4 are ok)
                const size_t MaxAlignmentCheckSuccessCount = 4;
#endif
                for (size_t i = 0; i < UnalignedCheckCount; i++)
                {
                    Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &(bogusUnalignedValues[i]), sizeof(BOOL), NULL);
                    if (Status == STATUS_DATATYPE_MISALIGNMENT)
                    {
                        alignmentErrorCount++;
                    }
                }
                // if there weren't enough alignment errors, we know that the API must be hooked and not checking alignment properly!
                if (UnalignedCheckCount - MaxAlignmentCheckSuccessCount > alignmentErrorCount)
                {
                    return TRUE;
                }

                // the legitimate call was successful, and the bogus call was unsuccessful, so return false (no detection) if the HideFromDebugger flag was properly set.
                // if the HideFromDebugger flag was not set, i.e. the NtSetInformationThread call lied to us about being successful, then return true (debugger/hook detected)
                return isThreadHidden.Value ? FALSE : TRUE;
            }
        }
    }
    else
    {
        // call failed, should've succeeded
        return TRUE;
    }

    // we didn't find any hooks.
    return FALSE;

看看反反调试是如何实现的

这里用scyllaHide的源码了解
可以看到,虽然scylla hide对参数进行了校验,但是,scyllaHide并未保存线程状态,并未对query函数进行hook来防止反调试通过set+query的方式检测调试器。

NTSTATUS NTAPI HookedNtSetInformationThread(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength)
{
    if (ThreadInformationClass == ThreadHideFromDebugger && ThreadInformationLength == 0) // NB: ThreadInformation is not checked, this is deliberate
    {
        if (ThreadHandle == NtCurrentThread ||
            HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess) == GetProcessIdByThreadHandle(ThreadHandle)) //thread inside this process?
        {
            return STATUS_SUCCESS;
        }
    }
    return HookDllData.dNtSetInformationThread(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength);
}

反 反反调试

我们要做步骤如下:
调用set
调用query进行校验
具体看代码和注释

基于https://www.cnblogs.com/kuangke/p/14702360.html修改
#include <windows.h>
#include <iostream>

typedef DWORD (WINAPI *ZW_SET_INFORMATION_THREAD)(HANDLE, DWORD, PVOID, ULONG);

typedef DWORD (WINAPI *ZW_QUERY_INFORMATION_THREAD)(HANDLE, DWORD, PVOID, ULONG,PULONG);

#define ThreadHideFromDebugger 17
VOID DisableDebugEvent(VOID)
{
    HINSTANCE hModule;
    ZW_SET_INFORMATION_THREAD ZwSetInformationThread;
    ZW_QUERY_INFORMATION_THREAD query;

    hModule = GetModuleHandleA("Ntdll");
    query = (ZW_QUERY_INFORMATION_THREAD)GetProcAddress(hModule, "ZwQueryInformationThread");
    ZwSetInformationThread =(ZW_SET_INFORMATION_THREAD)GetProcAddress(hModule, "ZwSetInformationThread");
    bool lRet = NULL;

    query(GetCurrentThread(), ThreadHideFromDebugger, &lRet,1, NULL);

    std::cout<<lRet<<std::endl;
    if(ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL)==0)
    {
     //调用query获取ThreadHideFromDebugger标志位的值。如果set调用成功,则lRet应该为true
     query(GetCurrentThread(), ThreadHideFromDebugger, &lRet,1, NULL);
     std::cout<<lRet<<std::endl;
     if(lRet==false)
     {
      std::cout<<"检测到调试"<<std::endl;
     }
    }
    else //如果调用失败,很有可能环境异常
    {
     std::cout<<"环境异常"<<std::endl;
    }

}

int main()
{

 DisableDebugEvent();
 getchar();
 return 0;
}

总结

这个反调试代码依旧十分弱小。

检测和校验,应该分开,不应该在一起。
校验不应该立即执行。
可以重载dll让反调试更隐秘,同时可以学习下al-khaser的写法,让反调试更完善。
可以通过scyllaHide和al-khaser进行学习。
ps:还有很多函数能这么玩。

另外

国产调试器cpudbg缺少个名字,坛友来取个名?最好有英文和中文。

效果

展示一下朋友发的测试sharp0d与scylla h1de图。
输出01为正常运行,输出00则为异常。

-7b83dcb12a1be023.jpg (172.59 KB, 下载次数: 3)

-7b83dcb12a1be023.jpg

7735951321334346.jpg (130.15 KB, 下载次数: 3)

7735951321334346.jpg

711b817da7146f8f.jpg (376.76 KB, 下载次数: 3)

711b817da7146f8f.jpg

r3反调试.zip

6.34 KB, 下载次数: 37, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 9威望 +2 吾爱币 +109 热心值 +8 收起 理由
努力加载中 + 1 + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
1MajorTom1 + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!
78zhanghao87 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
suko + 1 + 1 我很赞同!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
忆魂丶天雷 + 2 + 1 用心讨论,共获提升!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
witkidpro001 发表于 2023-1-30 09:51
阿里嘎多美羊羊桑
推荐
 楼主| 成熟的美羊羊 发表于 2023-1-26 15:03 |楼主
本帖最后由 成熟的美羊羊 于 2023-1-26 15:50 编辑
爱飞的猫 发表于 2023-1-23 18:47
还可以动态读取/映射一份 ntdll 到内存,处理下重定向后加载,然后调用,调用完后清理掉。

这类 Zw/Nt  ...

半斤八两在十年前开源过一个对抗safeEngine重载dll(shadow dll/api)的插件,可以基于插件源码写反反调试插件。
https://bbs.kanxue.com/thread-174005.htm

对抗shadow api的方式有很多,可以自己也重载一份已经能过反调试的ntdll,然后通过hook NtCreateFile使其加载我们自己的dll。
注意:重载dll是个高危权限操作。如果你读取dll的时候,或者readfile的时候没有做严格的参数校验,文件校验和防止堆栈溢出。最好不要使用,因为可以通过此来绕过其他反调试,读取程序数据。

点评

读内容的方法很多,到这里已经是攻防游戏了  详情 回复 发表于 2023-1-26 21:41
沙发
 楼主| 成熟的美羊羊 发表于 2023-1-19 23:14 |楼主
本帖最后由 成熟的美羊羊 于 2023-1-19 23:52 编辑

奇怪,手机端上传附件,选择文件的时候,找不到zip、7z的后缀文件。
附件上传百度云了,只有kb大小,应该下载很快。
通过百度网盘分享的文件:r3反调试.zi…链接:https://pan.baidu.com/s/1j5iF4SD3locGnNK9qMEW9g?pwd=nuil 提取码:nuil复制这段内容打开「百度网盘APP 即可获取」

点评

你试试重新编辑主题,看看会不会提示还有附件没使用?另外帖子底部好像还有几个图没插入到主题中。  详情 回复 发表于 2023-1-20 00:09
3#
Hmily 发表于 2023-1-20 00:09
成熟的美羊羊 发表于 2023-1-19 23:14
奇怪,手机端上传附件,选择文件的时候,找不到zip、7z的后缀文件。
附件上传百度云了,只有kb大小,应该 ...

你试试重新编辑主题,看看会不会提示还有附件没使用?另外帖子底部好像还有几个图没插入到主题中。
4#
mrkings 发表于 2023-1-20 00:09
就是非常牛逼~~~~~~~~~~
5#
 楼主| 成熟的美羊羊 发表于 2023-1-20 00:28 |楼主
Hmily 发表于 2023-1-20 00:09
你试试重新编辑主题,看看会不会提示还有附件没使用?另外帖子底部好像还有几个图没插入到主题中。

附件上传好了,是手机操作的问题,我选择显示文档文件了,导致只有txt,cpp文件出现。
图是之前编辑的时候,手一滑把浏览器给刷新了,保存了下来。

点评

好的,多余图片看看是插入还是多余,多余就删除吧。  详情 回复 发表于 2023-1-20 00:39
6#
Hmily 发表于 2023-1-20 00:39
成熟的美羊羊 发表于 2023-1-20 00:28
附件上传好了,是手机操作的问题,我选择显示文档文件了,导致只有txt,cpp文件出现。
图是之前编辑的时 ...

好的,多余图片看看是插入还是多余,多余就删除吧。
7#
灰灰。 发表于 2023-1-20 00:46
你贴的scyllaHide的hook代码看起来也没有很严格的校验参数啊,怎么感觉上面那个检测代码就能检测到他的hook了....
8#
freecat 发表于 2023-1-20 00:50
不错 学习一下 谢谢
9#
 楼主| 成熟的美羊羊 发表于 2023-1-20 01:03 |楼主
本帖最后由 成熟的美羊羊 于 2023-1-20 01:10 编辑

灰灰。 发表于 2023-1-20 00:46
你贴的scyllaHide的hook代码看起来也没有很严格的校验参数啊,怎么感觉上面那个检测代码就能检测到他的hook ...

找朋友测试了下,真的过不了。这就改,下次绝对不乱用词了。代码是用手机看的,感觉能过检测,就当它能过检测了。哈哈。
10#
灰灰。 发表于 2023-1-20 01:26
本帖最后由 灰灰。 于 2023-1-20 01:30 编辑
成熟的美羊羊 发表于 2023-1-20 01:03
灰灰。 发表于 2023-1-20 00:46
你贴的scyllaHide的hook代码看起来也没有很严格的校验参数啊,怎么感觉上 ...

刚测了下好像可以,主要是这个能检测到return isThreadHidden.Value ? FALSE : TRUE;
不过这个好像就是你最后发的方法哈哈
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2023-2-4 08:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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