首页
社区
课程
招聘
[原创]seccomp-bpf+ptrace实现修改系统调用原理(附demo)
2022-12-13 11:43 42484

[原创]seccomp-bpf+ptrace实现修改系统调用原理(附demo)

2022-12-13 11:43
42484

前言

目前绝大部分app都会频繁的使用syscall去获取设备指纹和做一些反调试,使用常规方式过反调试已经非常困难了,使用内存搜索svc指令已经不能满足需求了,开始学习了一下通过ptrace/ptrace配合seccomp来解决svc反调试难定位难绕过等问题。

seccomp

Linux 2.6.12中的导入了第一个版本的seccomp,通过向/proc/PID/seccomp接口中写入“1”来启动通过滤器只支持几个函数。

1
read(),write(),_exit(),sigreturn()

使用其他系统调用就会收到信号(SIGKILL)退出。测试代码如下:

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
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
 
void configure_seccomp() {
    printf("Configuring seccomp\n");
    prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT);
}
int main(int argc, char* argv[]) {
    int infd, outfd;
    if (argc < 3) {
        printf("Usage:\n\t%s <input path> <output_path>\n", argv[0]);
        return -1;
    }
    printf("Starting test seccomp Y/N?");
    char c = getchar();
    if (c == 'y' || c == 'Y') configure_seccomp();
    printf("Opening '%s' for reading\n", argv[1]);
    if ((infd = open(argv[1], O_RDONLY)) > 0) {
        ssize_t read_bytes;
        char buffer[1024];
        printf("Opening '%s' for writing\n", argv[2]);
        if ((outfd = open(argv[2], O_WRONLY | O_CREAT, 0644)) > 0) {
            while ((read_bytes = read(infd, &buffer, 1024)) > 0)
                write(outfd, &buffer, (ssize_t)read_bytes);
        }
        close(infd);
        close(outfd);
    }
    printf("End!\n");
 
    return 0;
}

可以看到执行到22行就结束了没执行到 Eed.
图片描述

seccomp-bpf

Seccomp-BPF(Berkeley Packet Filter)是Linux内核中的一种安全机制,用于限制进程对系统调用的访问权限。它主要用于防止恶意软件对系统的攻击,提高系统的安全性。

 

Seccomp-BPF使用BPF(Berkeley Packet Filter)技术来实现系统调用过滤,可以使用BPF程序指定哪些系统调用可以被进程访问,哪些不能。BPF程序由一组BPF指令组成,可以在系统调用执行之前对其进行检查,以决定是否允许执行该系统调用。

 

Seccomp-BPF提供了两种模式:白名单模式和黑名单模式。白名单模式允许所有系统调用,除非明确指定不允许的系统调用。黑名单模式禁止所有系统调用,除非明确指定允许的系统调用。这两种模式的选择取决于您的实际需求。

 

Seccomp-BPF提供了一个钩子函数,在系统调用执行之前会进入到这个函数,对系统调用进行检查,如果BPF程序允许执行该系统调用,则进程可以继续执行,否则会抛出一个异常。

1.BPF确定了一个可以在内核内部实现的虚拟机,该虚拟机具有以下特性:

1
2
3
4
5
6
7
8
9
10
11
简单指令集
    小型指令集
    所有的命令大小相一致
    实现过程简单、快速
只有分支向前指令
    程序是有向无环图(DAGs),没有循环
易于验证程序的有效性/安全性
    简单的指令集⇒可以验证操作码和参数
    可以检测死代码
    程序必须以 Return 结束
    BPF过滤器程序仅限于4096条指令

2.Seccomp-BPF 使用的也只是BPF的子集功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Conditional JMP(条件判断跳转)
    当匹配条件为真,跳转到true指定位置
    当 匹配条件为假,跳转到false指定位置
    跳转偏移量最大255
JMP(直接跳转)
    跳转目标是指令偏移量
    跳转 偏移量最大255
Load(数据读取)
    读取程序参数
    读取指定的16位内存地址
Store(数据存储)
    保存数据到指定的16位内存地址中
支持的运算
    + - * / & | ^ >> << !
返回值
    SECCOMP_RET_ALLOW -  允许继续使用系统调用
    SECCOMP_RET_KILL - 终止系统调用
    SECCOMP_RET_ERRNO -  返回设置的errno值
    SECCOMP_RET_TRACE -  通知附加的ptrace(如果存在)
    SECCOMP_RET_TRAP - 往进程发送 SIGSYS信号
最多只能有4096条命令
不能出现循环

Seccomp-BPF程序 接收以下结构作为输入参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * struct seccomp_data - the format the BPF program executes over.
 * @nr: the system call number
 * @arch: indicates system call convention as an AUDIT_ARCH_* value
 *        as defined in <linux/audit.h>.
 * @instruction_pointer: at the time of the system call.
 * @args: up to 6 system call arguments always stored as 64-bit values
 *        regardless of the architecture.
 */
struct seccomp_data {
    int nr;
    __u32 arch;
    __u64 instruction_pointer;
    __u64 args[6];
};

使用示例:

在这种情况下,seccomp-BPF 程序将允许使用 O_RDONLY 参数打开第一个调用 , 但是在使用 O_WRONLY | O_CREAT 参数调用 open 时终止程序。

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
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/unistd.h>
 
void configure_seccomp() {
  struct sock_filter filter [] = {
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))),
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_write, 0, 1),
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 0, 3),
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, args[1]))),
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, O_RDONLY, 0, 1),
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL)
  };
  struct sock_fprog prog = {
       .len = (unsigned short)(sizeof(filter) / sizeof (filter[0])),
       .filter = filter,
  };
 
  printf("Configuring seccomp\n");
  prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
  prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
}
 
int main(int argc, char* argv[]) {
  int infd, outfd;
  ssize_t read_bytes;
  char buffer[1024];
 
  if (argc < 3) {
    printf("Usage:\n\tdup_file <input path> <output_path>\n");
    return -1;
  }
  printf("Ducplicating file '%s' to '%s'\n", argv[1], argv[2]);
 
  configure_seccomp(); //配置seccomp
 
  printf("Opening '%s' for reading\n", argv[1]);
  if ((infd = open(argv[1], O_RDONLY)) > 0) {
    printf("Opening '%s' for writing\n", argv[2]);
    if ((outfd = open(argv[2], O_WRONLY | O_CREAT, 0644)) > 0) {
        while((read_bytes = read(infd, &buffer, 1024)) > 0)
          write(outfd, &buffer, (ssize_t)read_bytes);
    }
  }
  close(infd);
  close(outfd);
  return 0;
}

图片描述

使用ptrace修改系统调用

将getpid()的实现改为mkdir()的实现。主要是通过ptrace函数来跟踪子进程,获取其寄存器中的信息,然后根据需求替换对应的系统调用。

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
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/fcntl.h>
#include <syscall.h>
 
 
void die (const char *msg)
{
  perror(msg);
  exit(errno);
}
 
void attack()
{
  int rc;
  syscall(SYS_getpid, SYS_mkdir, "dir", 0777);
}
 
int main()
{
  int pid;
  struct user_regs_struct regs;
  switch( (pid = fork()) ) {
    case -1:  die("Failed fork");
    case 0:
 
              ptrace(PTRACE_TRACEME, 0, NULL, NULL);
              kill(getpid(), SIGSTOP);
              attack();
              return 0;
  }
 
  waitpid(pid, 0, 0);
 
  while(1) {
    int st;
 
    ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
    if (waitpid(pid, &st, __WALL) == -1) {
      break;
    }
 
    if (!(WIFSTOPPED(st) && WSTOPSIG(st) == SIGTRAP)) {
      break;
    }
 
    ptrace(PTRACE_GETREGS, pid, NULL, &regs);
    printf("orig_rax = %lld\n", regs.orig_rax);
 
 
    if (regs.rax != -ENOSYS) {
      continue;
    }
 
 
    if (regs.orig_rax == SYS_getpid) {
      regs.orig_rax = regs.rdi;
      regs.rdi = regs.rsi;
      regs.rsi = regs.rdx;
      regs.rdx = regs.r10;
      regs.r10 = regs.r8;
      regs.r8 = regs.r9;
      regs.r9 = 0;
      ptrace(PTRACE_SETREGS, pid, NULL, &regs);
    }
  }
  return 0;
}

使用seccomp-bpf+ptrace加ptrace修改系统调用

看一下main函数这里设置了跟踪openat系统调用子进程请求父进程附加 父进程开启ptrace+seccomp。

1.main

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
int main()
{
    pid_t pid;
    int status;
    if ((pid = fork()) == 0) {
        /* 目前是跟踪open系统调用 */
        struct sock_filter filter[] = {
            BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
            BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 1),
            BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE),
            BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
        };
        struct sock_fprog prog = {
            .filter = filter,
            .len = (unsigned short) (sizeof(filter)/sizeof(filter[0])),
        };
        //告诉父进程允许子进程跟踪
        ptrace(PTRACE_TRACEME, 0, 0, 0);
        /* 避免需要 CAP_SYS_ADMIN */
        if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
            perror("prctl(PR_SET_NO_NEW_PRIVS)");
            return 1;
        }
        if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
            perror("when setting seccomp filter");
            return 1;
        }
        kill(getpid(), SIGSTOP);
        ssize_t count;
        char buf[256];
        int fd;
        fd = syscall(__NR_openat,fd,"/data/local/tmp/tuzi.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/asdss.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/asda.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/TsdsaWO.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/sadas.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/sad.txt", O_RDONLY);
        syscall(__NR_openat,fd,"/data/local/tmp/asda.txt", O_RDONLY);
        //printf("fd : %d \n" ,fd);
        if (fd == -1) {
            perror("open");
            return 1;
        }
        while((count = syscall(__NR_read, fd, buf, sizeof(buf))) > 0) {
            syscall(__NR_write, STDOUT_FILENO, buf, count);
        }
        syscall(__NR_close, fd);
 
    } else {
        waitpid(pid, &status, 0);
        //尝试开启ptrace+seccomp
        ptrace(PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACESECCOMP);
        process_signals(pid);
        return 0;
    }
}

2.bpf结构

下面来解释一下bpf结构,BPF 被定义为一种虚拟机 (VM),它具有一个数据寄存器或累加器、一个索引寄存器和一个隐式程序计数器 (PC)。它的“汇编”指令被定义为具有以下格式的结构:

1
2
3
4
5
6
struct sock_filter {
    u_short code;
    u_char  jt;
    u_char  jf;
    u_long k;
};

有累加器,跳转等待码(操作码),jt和jf是跳转指令中使用的程序计数器的增量,而k是一个辅助值,其用法取决于代码编号。

 

BPFs有一个可寻址空间,其中的数据在网络情况下是一个数据包数据报,对于seccomp有一下结构:

1
2
3
4
5
6
7
struct seccomp_data {
    int   nr;                   /* System call number */
    __u32 arch;                 /* AUDIT_ARCH_* value
                                   (see <linux/audit.h>) */
    __u64 instruction_pointer;  /* CPU instruction pointer */
    __u64 args[6];              /* Up to 6 system call arguments */
};

所以bpfs在seccomp中做的是对这些数据进行操作并返回一个值告诉内核下一步做什么,比如:

1
2
允许进程执行调用(SECCOMP_RET_ALLOW)
终止(SECCOMP_RET_KILL)

详细见文档:seccomp文档
现在我们可以根据系统调用号和参数进行过滤,bpf过滤器被定义为一个sock_filter结构,其中每条都是一个bpf指令。

1
2
3
4
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 1),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),

BPF_STMT和BPF_JUMP是两个填充sock_filter结构的简单红。他在参数上有所不同。其中包括BPF_JUMP中的跳跃偏移量。在两种情况下。第一个参数都是操作码,作为助记符帮助:例如,第一个参数是使用绝对寻址(BPF_ABS) 将一个字 (BPF_W) 加载到累加器 (BPF_LD) 中。

 

第一条指令是要求VM将呼叫号码加载nr到累加器。第二条将与openat的系统调用号进行比较。如果他们相等(pc + o),则要求vm不修改计数器。因此运行第三条指令,否则跳转到PC+1,这是第四条指令(当执行到这条指令时,pc已经指向第三条指令)。因此如果这是一个开放的系统调用,我们将返回SECCOMP_RET_TRACE,这会调用跟踪器否则返回SECCOMP_RET_ALLOW,这将会让没有被跟踪的系统调用直接执行。

 

然后是第一次调用 prctl 设置PR_SET_NO_NEW_RPIVS,这会阻止子进程拥有比父进程更多的权限。他使用PR_SET_SECCOMP选择设置seccomp过滤器,不是root用户也可以使用,之后使用openat系统调用进行打开文件等操作。

 

父进程我设置了PTRACE_O_TRACESECCOMP 选项,当过滤器返回 SECCOMP_RET_TRACE 并将事件信号发送给跟踪器时,跟踪器将停止。此函数的另一个变化是我们不再需要设置 PTRACE_O_TRACESYSGOOD,因为我们被 seccomp 中断,而不是因为系统调用。

3.最终功能

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
static void process_signals(pid_t child)
{
    char file_to_redirect[256] = "/data/local/tmp/tuzi1.txt";
    char file_to_avoid[256] = "/data/local/tmp/tuzi.txt";
    int status;
    while(1) {
        char orig_file[PATH_MAX];
        struct user_pt_regs regs;
        struct iovec io;
        io.iov_base = &regs;
        io.iov_len = sizeof(regs);
        ptrace(PTRACE_CONT, child, 0, 0);
        waitpid(child, &status, 0);
        ptrace(PTRACE_GETREGSET, child, (void*)NT_PRSTATUS, &io);
        if (status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)) ){
            switch (regs.regs[8])
            {
            case __NR_openat:
                read_file(child, orig_file,regs);
                if (strcmp(file_to_avoid, orig_file) == 0){
                    putdata(child,regs.regs[1],file_to_redirect,strlen(file_to_avoid)+1);
            }
        }
 
        if (WIFEXITED(status)){
            break;
        }
    }
}

这里就很简单了获取到svc的信号后读取x8寄存器判断是否为openat的系统调用号,这里只对file_to_avoid进行了替换,看一下最终效果:
图片描述
可以看到不仅只对openat进行了监控也成功的将了第一次打开的文件
/data/local/tmp/tuzi.txt修改为了/data/local/tmp/tuzi1.txt。

结束

demo地址 github
完结撒花!

参考资料

proot

 

基于ptrace的Android系统调用跟踪&hook工具

 

SVC的TraceHook沙箱的实现&无痕Hook实现思路

 

ptrace を使用して seccomp による制限を回避してみる

 

ptrace(2) — Linux manual page

 

Seccomp and Seccomp-BPF

 

深入浅出 eBPF


阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

最后于 2023-1-11 14:54 被王麻子本人编辑 ,原因:
收藏
点赞34
打赏
分享
打赏 + 20.00雪花
打赏次数 2 雪花 + 20.00
 
赞赏  万里星河   +10.00 2023/01/08 瑕不掩瑜 感谢分享
赞赏  守哥哥   +10.00 2022/12/13 我也想要源码
最新回复 (86)
雪    币: 107
活跃值: (396)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Melanthe 2022-12-13 12:58
2
0
tql
雪    币: 507
活跃值: (1727)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
seeeseee 2022-12-13 15:33
3
0
tql
雪    币: 217
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ozmrnffd 2022-12-13 16:03
4
0
tql 能发一下全部源码吗
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-13 16:19
5
0
mb_ozmrnffd tql 能发一下全部源码吗
发了
雪    币: 15
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
一乐 2022-12-13 16:41
6
0
牛逼
雪    币: 217
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_ozmrnffd 2022-12-13 16:47
7
0
编译报arm_seccomp.h:3:47: error: unknown type name 'user_pt_regs'
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-13 16:51
8
0
mb_ozmrnffd 编译报arm_seccomp.h:3:47: error: unknown type name 'user_pt_regs'

user_pt_regs结构体在asm/ptrace应该没问题的吧。

最后于 2022-12-13 16:52 被王麻子本人编辑 ,原因:
雪    币: 1773
活跃值: (8930)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
你瞒我瞒 2022-12-13 17:04
9
0
窝草看不懂啊
雪    币: 986
活跃值: (6012)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
Ssssone 2 2022-12-13 19:52
10
0
我那个项目太简陋了,直接去看proot吧,ptrace相关api都封装好了,重定向的参数在栈上动态分配,少很多限制
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-13 21:23
11
0
Ssssone 我那个项目太简陋了,直接去看proot吧,ptrace相关api都封装好了,重定向的参数在栈上动态分配,少很多限制

我是先看的你的项目然后结合seccopm打的基础在去弄的proot,目前这个是放了个简单的demo出来给大家一个思路,你的那个项目还是给了我很大启发的。

最后于 2022-12-13 21:23 被王麻子本人编辑 ,原因:
雪    币: 5
活跃值: (985)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋一世的爱 2022-12-14 06:36
12
0
dwmo源码好像有问题,替换不成功,putdata后open error错误,源文件跟重定向文件都是存在的,路径也没错。另外while死循环,执行不到WIFEXITED(status),这个break语句始终没进来
雪    币: 5
活跃值: (985)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋一世的爱 2022-12-14 06:37
13
0
demo源码好像有问题,替换不成功,putdata后open error错误,源文件跟重定向文件都是存在的,路径也没错。另外while死循环,执行不到WIFEXITED(status),这个break语句始终没进来
雪    币: 5
活跃值: (985)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋一世的爱 2022-12-14 06:38
14
0
用打包注入方法测试的
雪    币: 345
活跃值: (618)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
全都是HOOK 2022-12-14 09:52
15
0
执行第二个prctl函数返回结果错误, 报: when setting seccomp filter: Invalid argument, 
请问这个怎么解决?
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 10:11
16
0
mb_czxkzsmv 执行第二个prctl函数返回结果错误, 报: when setting seccomp filter: Invalid argument, 请问这个怎么解决?

我重新上传了一下发的时候发错了好像,BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 2),改成BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 1)即可

最后于 2022-12-14 10:15 被王麻子本人编辑 ,原因:
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 10:13
17
0
恋一世的爱 demo源码好像有问题,替换不成功,putdata后open error错误,源文件跟重定向文件都是存在的,路径也没错。另外while死循环,执行不到WIFEXITED(status),这个break ...
啊?WIFEXITED?
雪    币: 5
活跃值: (985)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋一世的爱 2022-12-14 11:42
18
0
王麻子本人 啊?WIFEXITED?
是的,会有这2个问题,我昨天也改成了BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 1),也还是有我说的问题
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 12:13
19
0
恋一世的爱 是的,会有这2个问题,我昨天也改成了BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 1),也还是有我说的问题
我发新的了我这边是没问题的方便截图看一下吗
雪    币: 5
活跃值: (985)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恋一世的爱 2022-12-14 13:35
20
0

比如我要将1.txt重定向为2.txt


雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 15:17
21
0
恋一世的爱 比如我要将1.txt重定向为2.txt

你这个拦截是拦截成功了,修改参数也没啥问题,我demo写的非常简陋你可以试试换个目录,替换字符串你可以看到这个demo也是十分简陋看ptrace文档可以知道PTRACE_POKEDATA是

将字数据复制到被跟踪者的地址addr

这个在字符串长度相等和一些情况是可以正常运行的但是往往不能正常运行的,可以参考proot把重定向的参数在栈上动态分配

WIFEXITED的话就是的具体处理我这个demo没做只是子程序结束就结束循环而已。

最后于 2022-12-14 15:31 被王麻子本人编辑 ,原因:
雪    币: 345
活跃值: (618)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
全都是HOOK 2022-12-14 16:04
22
0
大佬, 请教一下, 为什么循环里面使用 
ptrace(PTRACE_CONT, child, 0, 0); 
而不是
ptrace(PTRACE_SYSCALL, child, 0, 0); 呢? 
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 16:16
23
0
mb_czxkzsmv 大佬, 请教一下, 为什么循环里面使用 ptrace(PTRACE_CONT, child, 0, 0); 而不是 ptrace(PTRACE_SYSCALL, child, 0, 0); ...
PTRACE_CONT是重新启动子进程让子进程继续运行下去。PTRACE_SYSCALL也是启动跟踪的子进程但是它只允许子进程执行一条系统调用后暂停。如果单纯使用ptrace去处理svc才需要使用PTRACE_SYSCALL,我们使用的是seccomp+ptrace,系统调用由seccomp进行过滤。
雪    币: 345
活跃值: (618)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
全都是HOOK 2022-12-14 17:24
24
0
王麻子本人 PTRACE_CONT是重新启动子进程让子进程继续运行下去。PTRACE_SYSCALL也是启动跟踪的子进程但是它只允许子进程执行一条系统调用后暂停。如果单纯使用ptrace去处理svc才需要使用PT ...
感谢解答, 豁然开朗!

如果要同时拦截多个系统调用 bpf 规则咋写啊, 我尝试添加了 __NR_write, __NR_openat , 虽然可以正常运行, 但是只拦截到了其中一个调用
雪    币: 776
活跃值: (3519)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
王麻子本人 2022-12-14 18:18
25
0
mb_czxkzsmv 感谢解答, 豁然开朗! 如果要同时拦截多个系统调用 bpf 规则咋写啊, 我尝试添加了 __NR_write, __NR_openat , 虽然可以正常运行, 但是只拦截到了其中一个调用
比较呆的写法是这样:添加一条jump指令
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_openat, 0, 2),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_read, 0, 1),
如果你需要过滤的系统调用比较多可以把每个调用的返回值放在一个数组里面
游客
登录 | 注册 方可回帖
返回