[原创] Android 内存执行ELF研究
2022-11-22 15:28:31 Author: bbs.pediy.com(查看原文) 阅读量:9 收藏

通过 Google 搜索 android linker namespace 看到官方文档 链接器命名空间 ,在 /linkerconfig/ld.config.txt 文件中配置了相关信息

通过字符串 “Using config section” 定位到 linker_config.cpp 的 parse_config_file 函数,在阅读代码后,我们上面的尝试为什么会失败,就已经有了答案。

虽然有 magisk,替换 system 分区下文件变得方便了起来,但是替换 linker 依然是一件麻烦事,动态 Hook 对于解决这个问题来说,我觉得更加方便。由于目标代码位置的特殊性,现有 inline hook, plt hook 轮子都不能取得比 linker 更早的执行时机。

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

bool mem_load(const std::string& image, char** argv, char** envp){

    // 创建内存文件,设置这个参数会在exec后自动关闭这个文件

    int fd = memfd_create("/jit-cache", MFD_CLOEXEC);

    ftruncate(fd, (long)image.size()); // 设置文件长度

    void *mem = mmap(nullptr, image.size(), PROT_WRITE, MAP_SHARED, fd, 0);

    memcpy(mem, image.data(), image.size());

    munmap(mem, image.size());

    // 此时已经将ELF内容写入到内存文件里面

    int pid = fork();

    if (pid < 0) {

        printf("mem_load failed\n"); // fork 失败

        return false;

    } else if (pid == 0) {

        // 这里是子进程,使用 PTRACE_TRACEME 主动建立连接

        ptrace(PTRACE_TRACEME);

        fexecve(fd, argv, envp);

    }

    // 这里是父进程

    int status;

    struct user_regs_struct regs{};

    struct iovec iov{};

    iov.iov_base = &regs;

    iov.iov_len = sizeof(user_regs_struct);

    while(true){

        wait(&status); // 等待子进程暂停

        if(WIFEXITED(status)){

            break; // 子进程退出

        }

        // 读取通用寄存器,系统调用号

        ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);

        if (regs.regs[8] == __NR_faccessat){ // access函数使用的系统调用号

            char path[] = "/memfd:";

            long word;

            // 注意,PTRACE_PEEKDATA 固定一次读取 sizeof(long) 字节长度的内容

            word = ptrace(PTRACE_PEEKDATA, pid, regs.regs[1], NULL);

            if (strcmp(path, (char*)&word) == 0){ // 判断是否是我们添加的目录

                ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL); // 单步执行,让系统调用执行完

                wait(nullptr); // 等待系统调用执行完

                ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov); // 读取寄存器

                regs.regs[0] = 0; // 修改返回值为寄存器

                ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov); // 修改寄存器

                ptrace(PTRACE_DETACH, pid, NULL, NULL); // detach 进程

                return true;

            }

        }

        ptrace(PTRACE_SYSCALL, pid, NULL, NULL); // 执行到下一个系统调用时暂停

    }

    return false;

}


文章来源: https://bbs.pediy.com/thread-275261.htm
如有侵权请联系:admin#unsafe.sh