首页
社区
课程
招聘
[原创]内核层隐藏inject so来规避maps扫描
2021-10-14 14:41 18937

[原创]内核层隐藏inject so来规避maps扫描

2021-10-14 14:41
18937

声明

学习交流, 请勿用于非法用途

防护系统如何检测你

续接上文内核层偷天换日之hook openat进行文件重定向

 

我们在分析一些软件的时候. 防护系统通过扫描/proc/self/maps,发现一些异常的so, 那么就会进行上报. 我们有很多方式解决这种问题, 但是我还是希望能够了解内核层底层原理的同时, 能够非常方便的解决此问题.

mmap文件映射需要注意的点

20211014134043

 

关于mmap的原理这里就不做详细的阐述, 这里面主要关注下文件映射, addr并不是完全相等映射, 只是相近而已. 通过官方的文档可以了解到.

 

20211014134217

maps文件简单介绍

20211014133558

 

进程的每段地址空间由struct vm_area_struct 描述。如上所示的每一行对应一个vm_area_struct结构体。一个文件可以映射到内存中,vm_area_struct的vm_file保存了文件描述符,这种映射称为有名映射,反之则为匿名映射

  1. 7857803000-785784b000

    本段内存映射的虚拟地址空间范围, 对应vm_area_struct中的vm_start和vm_end

  2. rw-p----权限

    r-读,w-写 x-可执行 p-私有,对应vm_flags。

  3. 00000000

    针对有名映射,指本段映射地址在文件中的偏移,对应vm_pgoff。对匿名映射而言,为vm_area_struct->vm_start

  4. fd:00

    所映射的文件所属设备的设备号,对应vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射为0。其中fd为主设备号,00为次设备号

  5. 00048000

    文件的索引节点号,对应vm_file->f_dentry->d_inode->i_ino,与ls –i显示的内容相符。匿名映射为0。

  6. /data/local/tmp/xxxx.so

    所映射的文件名。对有名映射而言,是映射的文件名,对匿名映射来说,是此段内存在进程中的作用。[stack]表示本段内存作为栈来使用,[heap]作为堆来使用,其他情况则为无。

cat /proc/self/map底层实现

在内核层fs系统实现了/proc/self/maps设备驱动, 其中核心实现在task_mm.c的show_map_vma函数中.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static void show_vma_header_prefix2(struct seq_file *m,
                   unsigned long start, unsigned long end,
                   vm_flags_t flags, unsigned long long pgoff,
                   dev_t dev, unsigned long ino)
{
    seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
    seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu ",
           start,
           end,
    //文件权限相关, 这里把可执行权限隐藏掉, 伪造成匿名映射.或者其他系统映射都可以
           flags & VM_READ ? 'r' : '-',
           flags & VM_WRITE ? 'w' : '-',
           '-',//flags & VM_EXEC ? 'x' : '-',
           flags & VM_MAYSHARE ? 's' : 'p',
           0,//pgoff,
           0, 0, 0);//MAJOR(dev), MINOR(dev), ino);
}
 
static void
show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
{
    struct mm_struct *mm = vma->vm_mm;
    struct file *file = vma->vm_file;
    vm_flags_t flags = vma->vm_flags;
    unsigned long ino = 0;
    unsigned long long pgoff = 0;
    unsigned long start, end;
    dev_t dev = 0;
    const char *name = NULL;
    char path_buf[PATH_MAX] = {0};
 
    if (file) {
        struct inode *inode = file_inode(vma->vm_file);
        dev = inode->i_sb->s_dev;
        ino = inode->i_ino;
        pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
    }
 
    start = vma->vm_start;
    end = vma->vm_end;
    if(file) {
        show_vma_header_prefix2(m, start, end, flags, pgoff, dev, ino);
    } else {
        show_vma_header_prefix(m, start, end, flags, pgoff, dev, ino);
    }   
 
    /*
     * Print the dentry name for named mappings, and a
     * special [heap] marker for the heap:
     */
    if (file) {
        // 获取文件名称
        char *path = d_path(&file->f_path, path_buf, sizeof(path_buf));
        seq_pad(m, ' ');
        if(strlen(path) > 0) {           
            if(strstr(path, "xxxx.so")) {
                // 过滤xxxx.so
                goto done;
            }
        }               
        seq_file_path(m, file, "\n");       
        goto done;
    }
 
    if (vma->vm_ops && vma->vm_ops->name) {
        name = vma->vm_ops->name(vma);
        if (name)
            goto done;
    }
 
    name = arch_vma_name(vma);
    if (!name) {
        if (!mm) {
            name = "[vdso]";
            goto done;
        }
 
        if (vma->vm_start <= mm->brk &&
            vma->vm_end >= mm->start_brk) {
            name = "[heap]";
            goto done;
        }
 
        if (is_stack(vma)) {
            name = "[stack]";
            goto done;
        }
 
        if (vma_get_anon_name(vma)) {
            seq_pad(m, ' ');
            seq_print_vma_name(m, vma);
        }
    }
 
done:
    if (name) {
        seq_pad(m, ' ');
        seq_puts(m, name);
    }
    seq_putc(m, '\n');
}

结果验证

通过上面的信息处理后, 所有的都没有对应的可执行权限了, 有了这种方法, 你就可以定制你自己的隐藏技术, maps文件便由你掌控

 

20211014140205

对应实现

https://github.com/yhnu/op7t/tree/dev/hide_inject_so


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2021-10-14 14:43 被github_yhnu编辑 ,原因:
收藏
点赞4
打赏
分享
最新回复 (24)
雪    币: 202
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
leftlizhi 2021-10-14 18:36
2
0
这个方案虽然不错,但是其实实际上人家会考虑如何从ROM是不是真的入手,而这个玩意的难度比maps蛋疼多了。另外,可能还得考虑linker下面2组获取modules的数据
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
github_yhnu 2021-10-15 10:04
3
0
leftlizhi 这个方案虽然不错,但是其实实际上人家会考虑如何从ROM是不是真的入手,而这个玩意的难度比maps蛋疼多了。另外,可能还得考虑linker下面2组获取modules的数据
rom真不真好解决后面再分享. 可能还得考虑linker下面2组获取modules的数据, 针对这个问题能否详细阐述下. 没理解
雪    币: 1867
活跃值: (3703)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
virjar 1 2021-10-15 12:10
4
0

riru不是本身就做了hide么?调用一下他的函数就可以了吧?似乎不需要在内核层面修改?


https://github.com/RikkaApps/Riru/blob/8b337d16dd7f104f543e54ad7d82149018df3298/riru/src/main/cpp/hide/hide.cpp#L93

最后于 2021-10-15 12:13 被virjar编辑 ,原因:
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-10-15 12:37
5
0
你都修改内核了,为什么不直接加个hide整个vma的系统调用,被隐藏的segment直接从maps里消失不是更好?
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
github_yhnu 2021-10-18 09:03
6
0
不吃早饭 你都修改内核了,为什么不直接加个hide整个vma的系统调用,被隐藏的segment直接从maps里消失不是更好?
好主意,我已经按照这种模式改了
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
github_yhnu 2021-10-18 09:04
7
0
virjar riru不是本身就做了hide么?调用一下他的函数就可以了吧?似乎不需要在内核层面修改?https://github.com/RikkaApps/Riru/blob/8b337d16dd7f104f5 ...
学习了,一段匿名空间但是有可执行权限, 在防护系统看来是否很可疑?
雪    币: 1867
活跃值: (3703)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
virjar 1 2021-10-18 10:24
8
0
github_yhnu 学习了,一段匿名空间但是有可执行权限, 在防护系统看来是否很可疑?

感觉还好吧,目前的风控sdk大多还是扫描so文件特征。

最后于 2021-10-18 10:29 被virjar编辑 ,原因:
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-10-18 12:23
9
0
github_yhnu 学习了,一段匿名空间但是有可执行权限, 在防护系统看来是否很可疑?
很可疑,某讯就会检测此类内存段
雪    币: 4448
活跃值: (3481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 2021-10-18 12:49
10
0
不吃早饭 很可疑,某讯就会检测此类内存段
那么如何过内存段是可执行属性检测呢 如果代码需要长时间驻留在内存里执行的话
雪    币: 4448
活跃值: (3481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 2021-10-18 12:51
11
0
不吃早饭 很可疑,某讯就会检测此类内存段
要是能操控cpu就好了
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-10-18 12:57
12
0
木志本柯 那么如何过内存段是可执行属性检测呢 如果代码需要长时间驻留在内存里执行的话

用户层的话就是拦截io操作,重定向maps,smaps文件,隐藏map_files目录,实现难度较高。内核层的话隐藏对应vma即可,难度较低。

最后于 2021-10-18 12:58 被不吃早饭编辑 ,原因:
雪    币: 4448
活跃值: (3481)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 2021-10-18 12:59
13
0
Android能不能无痕hook内存属性检测api 返回假的属性值
雪    币: 1867
活跃值: (3703)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
virjar 1 2021-10-18 14:56
14
0
不吃早饭 木志本柯 那么如何过内存段是可执行属性检测呢 如果代码需要长时间驻留在内存里执行的话 用户层的话就是拦截io操作,重定向maps,smaps文件 ...
重定向maps文件在很多情况导致各种闪退。不太好操作。fd反查可以拿到重定向的真实的路径
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-10-18 15:09
15
0
virjar 重定向maps文件在很多情况导致各种闪退。不太好操作。fd反查可以拿到重定向的真实的路径

重定向包括对stat系列函数的hook,许多io相关的操作都需要,例如inotify_add_watch等,当然不只是open/openat。另外重定向只要处理完善并不会出现频繁的闪退,至少我这里没有遇到

最后于 2021-10-18 15:12 被不吃早饭编辑 ,原因:
雪    币: 29
活跃值: (5115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
不吃早饭 2021-10-18 15:15
16
0
virjar 重定向maps文件在很多情况导致各种闪退。不太好操作。fd反查可以拿到重定向的真实的路径
一般来讲io重定向的主要问题是内联syscall,这种情况下如果不做专门处理确实会产生逃逸,不过实际上也有解决方案
雪    币: 1867
活跃值: (3703)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
virjar 1 2021-10-19 09:39
17
0
不吃早饭 一般来讲io重定向的主要问题是内联syscall,这种情况下如果不做专门处理确实会产生逃逸,不过实际上也有解决方案
ptrace?还是扫描全部可执行区域?
我测试普通的io重定向没有出现过闪退,但是只要对maps进行fd重定向,确实有一定概率闪退。可能真的没有处理完善吧
雪    币: 64
活跃值: (3694)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
RiDiN 2021-10-22 11:00
18
0

-

最后于 2023-1-31 10:33 被RiDiN编辑 ,原因:
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
github_yhnu 2021-10-23 17:53
19
0
RiDiN https://github.com/rev1si0n/android_kernel_oneplus_msm8998/commit/d3ad2f4925f58df46f787ce46c950fbc15 ...
欢迎添加更多功能互相学习
雪    币: 202
活跃值: (118)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
leftlizhi 2021-10-26 18:44
20
0
github_yhnu rom真不真好解决后面再分享. 可能还得考虑linker下面2组获取modules的数据, 针对这个问题能否详细阐述下. 没理解
就是,先生成一个进程,再attach它,在检测它的tracerid,看有无自相矛盾
雪    币: 242
活跃值: (435)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
tcc0lin 2023-8-10 10:20
21
0
不吃早饭 你都修改内核了,为什么不直接加个hide整个vma的系统调用,被隐藏的segment直接从maps里消失不是更好?
大佬,这种方法是什么意思呢?是直接删除有rwxp权限的那一行吗?如果直接去掉的话maps中的内存地址不连续了
雪    币: 2889
活跃值: (2887)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
安和桥南 2023-8-10 11:24
22
0
木志本柯 那么如何过内存段是可执行属性检测呢 如果代码需要长时间驻留在内存里执行的话
简单用户态可以参考 CobaltStrike,不执行的时候修改内存块权限,唤醒的时候再改一遍,要么就按内核 rootkit做 hook 内核函数写一个 filter
雪    币: 0
活跃值: (352)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
袁伟业 2023-12-13 15:56
23
0
好像效果不大,用cat去读maps会隐藏,但是程序都是用fopen去读maps,隐藏不了
雪    币: 242
活跃值: (435)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
tcc0lin 2024-2-18 19:15
24
0
不吃早饭 很可疑,某讯就会检测此类内存段
是的,检测inline hook特征的
雪    币: 19516
活跃值: (29191)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2024-2-18 20:42
25
1
感谢分享
游客
登录 | 注册 方可回帖
返回