AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 464539
Accepted
Billy
Billy
Asked: 2018-08-24 17:42:09 +0800 CST2018-08-24 17:42:09 +0800 CST 2018-08-24 17:42:09 +0800 CST

如何使用 ptrace() 使特定进程执行给定的可执行文件?

  • 772

我正在尝试将嵌入式 Linux 系统的 init 进程强制执行到exec()我自己的 init 程序(systemd),以便我可以在将外部文件系统写入系统的闪存之前对其进行测试(并冒着使设备变砖的风险)。使用 GDB,我可以运行 command gdb --pid=1,然后在那个 shell 类型中运行call execl("/lib/systemd/systemd", "systemd", 0)(它完全按照我的需要工作),但是我没有足够的空间将 GDB 放在系统的闪存上。

我想知道ptrace()GDB 的call命令到底使用了什么调用,以便我可以在自己的简单 C 程序中实现它。

我试图strace找出ptrace()GDB 使用了什么调用,但生成的文件有 172,031 行长。我也尝试过查看它的源代码,但是文件太多,无法找到我要查找的内容。

该设备运行 Linux 内核版本 3.10.0,配置可在此处获得:https ://pastebin.com/rk0Zux62

linux system-calls
  • 1 1 个回答
  • 951 Views

1 个回答

  • Voted
  1. Best Answer
    Joseph Sible-Reinstate Monica
    2018-09-03T21:29:57+08:002018-09-03T21:29:57+08:00

    这是一个应该执行此操作的 C 程序。注意几个已知问题:

    • 应该可能使用 memcpy 而不是严格的别名违规
    • 使用自己的环境变量而不是旧的tracee的环境变量
    • 如果被跟踪者没有进行任何系统调用,这将永远无法做任何事情
    • 不检查跟踪对象何时停止以确保它确实是系统调用停止而不是信号停止或其他东西
    • 应该在 syscall-exit-stop 而不是 syscall-enter-stop 中设置 IP
    • 不对 execve 参数进行任何完整性检查(这样做是 execveat 的好机会)
    • 完全不可移植(CONFIG_ARM_THUMB许多其他东西中的硬代码)
    • 如果任何系统调用无法正常工作,则使进程处于可能崩溃的状态

    编译它-fno-strict-aliasing,然后运行它./a.out 1 /lib/systemd/systemd systemd。

    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/ptrace.h>
    #include <linux/ptrace.h>
    #include <sys/wait.h>
    #include <sys/user.h>
    #include <sys/syscall.h>
    #include <sys/mman.h>
    
    #define CONFIG_ARM_THUMB
    
    #ifdef CONFIG_ARM_THUMB
    #define thumb_mode(regs) \
            (((regs)->ARM_cpsr & PSR_T_BIT))
    #else
    #define thumb_mode(regs) (0)
    #endif
    
    extern char **environ;
    
    static pid_t pid;
    
    /* The length of a string, plus the null terminator, rounded up to the nearest sizeof(long). */
    size_t str_size(char *str) {
            size_t len = strlen(str);
            return len + sizeof(long) - len % sizeof(long);
    }
    
    void must_poke(long addr, long data) {
            if(ptrace(PTRACE_POKEDATA, pid, (void*)addr, (void*)data)) {
                    perror("ptrace(PTRACE_POKEDATA, ...)");
                    exit(1);
            }
    }
    
    void must_poke_multi(long addr, long* data, size_t len) {
            size_t i;
            for(i = 0; i < len; ++i) {
                    must_poke(addr + i * sizeof(long), data[i]);
            }
    }
    
    long must_poke_string(long addr, char* str) {
            size_t len = str_size(str);
            size_t longs_len = len / sizeof(long);
            char *more_nulls_str = malloc(len);
            memset(more_nulls_str + len - sizeof(long), 0, sizeof(long)); /* initialize the bit we might not copy over */
            strcpy(more_nulls_str, str);
            must_poke_multi(addr, (long*)more_nulls_str, longs_len);
            free(more_nulls_str);
            return addr + len;
    }
    
    int main(int argc, char** argv) {
            struct user_regs regs;
            int i, envc;
            unsigned long mmap_base;
            size_t mmap_string_offset, mmap_argv_offset, mmap_envp_offset;
            size_t mmap_len = 2 * sizeof(char*); /* for the NULLs at the end of argv and envp */
    
            if(argc < 3) {
                    fprintf(stderr, "Usage: %s <pid> <executable image> <args...>\n", argv[0]);
                    return 1;
            }
    
            pid = strtol(argv[1], NULL, 10);
    
            /* for the image name */
            mmap_len += str_size(argv[2]);
    
            for(i = 3; i < argc; ++i) {
                    /* for the pointer in argv plus the string itself */
                    mmap_len += sizeof(char*) + str_size(argv[i]);
            }
    
            for(i = 0; environ[i]; ++i) {
                    /* for the pointer in envp plus the string itself */
                    mmap_len += sizeof(char*) + str_size(environ[i]);
            }
            envc = i;
    
            if(ptrace(PTRACE_ATTACH, pid, 0, 0)) {
                    perror("ptrace(PTRACE_ATTACH, ...)");
                    return 1;
            }
            if(waitid(P_PID, pid, NULL, WSTOPPED)) {
                    perror("waitid");
                    return 1;
            }
    
            /* Stop at whatever syscall happens to be next */
            if(ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
                    perror("ptrace(PTRACE_SYSCALL, ...)");
                    return 1;
            }
            printf("Waiting for the target process to make a syscall...\n");
            if(waitid(P_PID, pid, NULL, WSTOPPED)) {
                    perror("waitid");
                    return 1;
            }
            printf("Target made a syscall. Proceeding with injection.\n");
    
            if(ptrace(PTRACE_GETREGS, pid, 0, &regs)) {
                    perror("ptrace(PTRACE_GETREGS, ...)");
                    return 1;
            }
    
            /* End up back on the syscall instruction so we can use it again */
            regs.ARM_pc -= (thumb_mode(&regs) ? 2 : 4);
    
            /* mmap some space for the exec parameters */
            regs.ARM_r0 = (long)0;
            regs.ARM_r1 = (long)mmap_len;
            regs.ARM_r2 = (long)(PROT_READ|PROT_WRITE);
            regs.ARM_r3 = (long)(MAP_PRIVATE|MAP_ANONYMOUS);
            regs.ARM_r4 = (long)-1;
            regs.ARM_r5 = (long)0;
            if(ptrace(PTRACE_SETREGS, pid, 0, &regs)) {
                    perror("ptrace(PTRACE_SETREGS, ...)");
                    return 1;
            }
            if(ptrace(PTRACE_SET_SYSCALL, pid, 0, SYS_mmap2)) {
                    perror("ptrace(PTRACE_SET_SYSCALL, ...)");
                    return 1;
            }
    
            /* jump to the end of the syscall */
            if(ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
                    perror("ptrace(PTRACE_SYSCALL, ...)");
                    return 1;
            }
            if(waitid(P_PID, pid, NULL, WSTOPPED)) {
                    perror("waitid");
                    return 1;
            }
    
            /* make sure it worked and get the memory address */
            if(ptrace(PTRACE_GETREGS, pid, 0, &regs)) {
                    perror("ptrace(PTRACE_GETREGS, ...)");
                    return 1;
            }
    
            if(regs.ARM_r0 > -4096UL) {
                    errno = -regs.ARM_r0;
                    perror("traced process: mmap");
                    return 1;
            }
            mmap_base = regs.ARM_r0;
    
            /* set up the execve args in memory */
            mmap_argv_offset = must_poke_string(mmap_base, argv[2]);
    
            mmap_string_offset = mmap_argv_offset + (argc - 2) * sizeof(char*); /* don't forget the null pointer */
            for(i = 0; i < argc - 3; ++i) {
                    must_poke(mmap_argv_offset + i * sizeof(char*), mmap_string_offset);
                    mmap_string_offset = must_poke_string(mmap_string_offset, argv[i + 3]);
            }
            must_poke(mmap_argv_offset + (argc - 3) * sizeof(char*), 0);
            mmap_envp_offset = mmap_string_offset;
            mmap_string_offset = mmap_envp_offset + (envc + 1) * sizeof(char*); /* don't forget the null pointer */
            for(i = 0; i < envc; ++i) {
                    must_poke(mmap_envp_offset + i * sizeof(char*), mmap_string_offset);
                    mmap_string_offset = must_poke_string(mmap_string_offset, environ[i]);
            }
            must_poke(mmap_envp_offset + envc * sizeof(char*), 0);
    
            /* jump to the start of the next syscall (same PC since we reset it) */
            if(ptrace(PTRACE_SYSCALL, pid, 0, 0)) {
                    perror("ptrace(PTRACE_SYSCALL, ...)");
                    return 1;
            }
            if(waitid(P_PID, pid, NULL, WSTOPPED)) {
                    perror("waitid");
                    return 1;
            }
    
            if(ptrace(PTRACE_GETREGS, pid, 0, &regs)) {
                    perror("ptrace(PTRACE_GETREGS, ...)");
                    return 1;
            }
    
            /* call execve */
            regs.ARM_r0 = (long)mmap_base;
            regs.ARM_r1 = (long)mmap_argv_offset;
            regs.ARM_r2 = (long)mmap_envp_offset;
            if(ptrace(PTRACE_SETREGS, pid, 0, &regs)) {
                    perror("ptrace(PTRACE_SETREGS, ...)");
                    return 1;
            }
            if(ptrace(PTRACE_SET_SYSCALL, pid, 0, SYS_execve)) {
                    perror("ptrace(PTRACE_SET_SYSCALL, ...)");
                    return 1;
            }
    
            /* and done. */
            if(ptrace(PTRACE_DETACH, pid, 0, 0)) {
                    perror("ptrace(PTRACE_DETACH, ...)");
                    return 1;
            }
            return 0;
    }
    

    我通过 qemu-system-arm 使用 3.2.0-4 内核和来自https://people.debian.org/~aurel32/qemu/armel/的 wheezy 用户区开发和测试了它

    • 3

相关问题

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve