首页
社区
课程
招聘
3
[原创]ring3检测rootkit技巧Windows篇--发现隐藏文件、进程和端口(有源码)
发表于: 2025-1-16 11:07 3375

[原创]ring3检测rootkit技巧Windows篇--发现隐藏文件、进程和端口(有源码)

2025-1-16 11:07
3375

1.前言

在安全应急响应中,我们接触过高级的rootkit木马,常规的终端取证通常不容易检测到它的进程和文件等,因为rootkit在ring0的一些位置过滤了一些关于它的信息。但在实践中,我们发现过一些技巧,在ring3也能检测出这些高级rootkit隐藏的信息,在这篇文章中,我将分享一些纯粹在ring3检测windows下被rootkit隐藏的进程、端口、文件的技巧,以及开发完成的工具和完整源代码(见文章底部链接),希望对应急响应同行的小伙伴有帮助。Linux下的rootkit的检测思路也大部分类似,期待我们下一篇针对Linux的检测工具和源码。

2.文件检测

最方便且好用的检查隐藏文件的工具,可能已经安装在你系统上了,我想说的是7-zip。

我先说说rootkit针对恶意文件的隐蔽手法主要有几种吧。

  • 常见的一种是通过简单hook枚举文件的API使得应用层看不到这个文件。
  • 还见一种也是hook,但它不隐藏文件,它把访问恶意.sys文件“重定向”到另一个微软自带的.sys文件,用户在应用层查看该文件属性、HASH都会认为是合法的。
  • 稍微高级的不是hook接近应用层的API了,是让恶意文件所在分区设备(\\?\Volume{GUID})的磁盘数据目录信息看不到这个文件。

2.1检查隐藏文件的原理

针对各种hook方法,我想最有效的检查隐藏文件的方法就是自己解析磁盘数据,不走常规的枚举文件的API。那有什么简单的编程方法实现自己解析磁盘数据呢?见下图,

文件

用了很多年的7zip,可能你还不知道它可以这么用,支持直接打开\\.\PhysicalDrive0。7-zip是开源的,很方便拿过来开发,自己编程解析\\.\PhysicalDrive0获得磁盘目录信息。

借着7-zip的源码,我开发了一个工具rawdir,很快就实现了Everything导出全盘目录信息的功能。但和Everything不同的是,我从\\.\PhysicalDrive0设备获取磁盘信息,而不是从\\?\Volume{GUID}设备。

隐藏文件的检测原理,简言之,rawdir获取文件列表信息时,会从最底层磁盘设备\\.\PhysicalDrive0去解析目录信息。然后再使用常用的系统API列举目录信息,从两者结果进行对比发现差异,把不存在或大小不一致的列出来,我在自己系统目录下检测没有隐藏文件,工具效果见下图,

文件

最后要注意的问题有:

  • 直接解析PhysicalDrive0获得的文件大小存在未Flush的情况,特别是一些LOG文件,会与FindFirstFile看到的大小不一致。可重点关注可执行文件(dll、exe、sys)类型的异常。
  • FindFirstFile会因为权限问题,有些目录文件列不出来,而直接解析PhysicalDrive0可以看到那些文件,但并不是被“隐藏”的文件。

3.进程检测

也是先说说rootkit针对进程的隐蔽手法有哪些?可能太多方法了,说2个最常见的吧,然后我们来对付这些手法。

  • 常见的一种是通过简单hook NtQuerySystemInformation 过滤查询SystemProcessinformation的结果。
  • 修改ActiveProcessLinks。

其他例如修改PspCidTable等可能还相对少见,况且现在总体行情来讲,隐藏进程的事情应该也比较少的,都是注入代码,纯shellcode等方式,这些恶意内存代码的检测技巧也有很多,这篇就不展开,敬请期待后续篇吧。

我们不进入ring0,那发现隐藏进程总体思路就是多尝试一些不同的获取进程列表的方法,然后与NtQuerySystemInformation(SystemProcessinformation)的结果对比,如果rootkit没有过滤某些地方,那它就暴露了。

通过查看早期windows源码XPSP1/NT/base/ntos/ex/sysinfo.c里的NtQuerySystemInformation源码,我就发现,常规获取进程列表的API最终是用ExpGetProcessInformation函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
case SystemProcessInformation:
case SystemExtendedProcessInformation:
    {
        BOOLEAN ExtendedInformation;
 
        if (SystemInformationClass == SystemProcessInformation ) {
            ExtendedInformation = FALSE;
        } else {
            ExtendedInformation = TRUE;
        }
 
        Status = ExpGetProcessInformation (SystemInformation,
                                       SystemInformationLength,
                                       &Length,
                                       NULL,
                                       ExtendedInformation);
 
        if (ARGUMENT_PRESENT( ReturnLength )) {
            *ReturnLength = Length;
        }
    }
 
    break;
 
case SystemSessionProcessInformation:
//...省略
    Status = ExpGetProcessInformation (ProcessInformation,
                                       ProcessInformationLength,
                                       &Length,
                                       &SessionId,
                                       FALSE);

NtQuerySystemInformation里除了SystemProcessinformation类,还有SystemExtendedProcessInformation和SystemSessionProcessInformation也会调用ExpGetProcessInformation可以查询到所有进程。如果rootkit作者只过滤SystemProcessinformation类的结果,那他可能真的有点疏忽了。我们就可以针对这种疏忽进行检测。

还有一种方法,用从0一直按步长4递增PID,然后用测试该PID的有效性的方法来发现所有PID。但实际测试有坑,一些已经结束的进程也能成功OpenProcess,于是我用了下面代码2个简单方法来判断这些进程是Active状态的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
BOOL isProcessStillActive1(DWORD pid)
{
    HANDLE hProc;
    hProc = OpenProcess(SYNCHRONIZE, FALSE, pid);
    if (hProc) {
        DWORD w = WaitForSingleObject(hProc, 0);
        CloseHandle(hProc);
        if (w == WAIT_TIMEOUT)
        {
            return 1;
        }
    }
    return 0;
}
 
BOOL isProcessStillActive2(DWORD pid)
{
    static int vista = -1;
    if (vista == -1) {
        vista = IsWindowsVistaOrGreater();
    }
     
    HANDLE hProcess = OpenProcess(vista ? PROCESS_QUERY_LIMITED_INFORMATION : PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (hProcess) {
        DWORD exitCode;
        if (GetExitCodeProcess(hProcess, &exitCode)) {
            if (exitCode == STILL_ACTIVE) {
                CloseHandle(hProcess);
                return 1;
            }
        }
        CloseHandle(hProcess);
    }
    return 0;
}

目前就有了3种相对不常规的方法来获取进程列表了,然后再随便选一种常规的API例如CreateToolhelp32Snapshot获取进程列表,它最后也是走NtQuerySystemInformation(SystemProcessinformation)查询结果。然后我们通过对比来发现一些可疑隐藏进程。下面是已经实现的ps工具效果图,通过修改ActiveProcessLinks隐藏的进程,然后被爆搜PID发现。

进程

4.端口检测

rootkit隐藏的手法如果还是基于hook的话,不管是ZwDeviceIoControlFile过滤还是哪里hook过滤,都挡不住暴力探索端口的方法吧。

这里还是采用爆破的思路,毕竟端口号也就1-65535个,使用暴力bind端口从1-65535,可以非常快速的发现一批绑定失败的端口。然后再用常用的API获取系统本地端口信息,如果绑定失败的端口却不在正常获取的端口列表里面,则是可疑的。针对TCP端口的,因为有些ESTABLISHED和CLOSE_WAIT也会占用本地端口,再次用connect确认下本地端口是否可以连接成功。这样一来

总结

这篇文章的思路全是基于ring3实现的,虽然说,在有rootkit的系统上,应用层看到的数据都是不可信的,然而对抗都是有成本的,更何况乱拳还能打死老师傅呢,再怎么高级背景的rootkit,也会挡不住一些爆搜探测的方法。因此我认为这里一些检测思路还是很有意义的。更重要的是这些工具已经写好且开源了,欢迎交流,给工具注入更多检测思路。

链接

项目链接 https://github.com/threatexpert/atrk-win

仓库链接 https://github.com/threatexpert?头像有二维码,欢迎加入交流


[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 3
支持
分享
赞赏记录
参与人
雪币
留言
时间
_Smile`Melo
期待更多优质内容的分享,论坛有你更精彩!
2025-1-24 16:36
新气象
你的帖子非常有用,感谢分享!
2025-1-17 15:39
雪梨不是雪莉
为你点赞!
2025-1-16 11:59
最新回复 (4)
雪    币: 62
活跃值: (403)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感觉有用,但不多(
2025-1-16 11:59
0
雪    币: 4006
活跃值: (2070)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
注入系统进程内存然后磁盘上的文件自删除,又怎么检测到
2025-1-16 16:00
0
雪    币: 226
活跃值: (220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
gailium 注入系统进程内存然后磁盘上的文件自删除,又怎么检测到
你说的情况属于“进程恶意内存代码的检测”范涛,这篇没有展开。恶意内存代码还得对抗手法,例如是否抹去PE头,是否有线程在执行内存代码等等
2025-1-16 16:07
0
雪    币: 226
活跃值: (220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
刚发现通过SystemExtendedHandleInformation的查询,也能搜集到几乎全部的PID信息。github已更新ps工具,添加方法5
2025-1-16 18:08
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册