【问题标题】:Mac OS (10.13.1) task_for_pid for forked process "(os/kern) failure"Mac OS (10.13.1) task_for_pid for fork process "(os/kern) failure"
【发布时间】:2018-05-21 18:32:13
【问题描述】:

上次我尝试编写简单的基因模糊器(严格使用 Mac OS,只是为了好玩)。我的想法是这样的:

-> 控制分叉进程的主程序

--> 分叉进程从磁盘加载二进制代码并跳转。

-> 父请求任务 (task_for_pid(mach_task_self(),childPID,&task))

-> parent 尝试捕获陷阱(0xcc),检查我们之前是否去过那里,就像 AFL 的工作原理(当然是简化)

--> 子加载一些原始二进制代码(在我的示例中必须是 System V ABI)

我得到如下错误:

16:10|domin568[15] ~/Desktop/experiments/Instrumentation $ ./run.sh
PARENT 3866
task_for_pid() failed with message (os/kern) failure !
CHILD 3867

运行.sh:

#!/bin/sh
clang -sectcreate __TEXT __info_plist Info.plist -o server server.c
codesign -s instrument ./server
./server

“instrument”出现在我的钥匙串中,并且始终信任代码唱歌,所以我认为它不应该是这样的。

Info.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>SecTaskAccess</key>
    <array>
        <string>allowed</string>
        <string>debug</string>
    </array>
</dict>
</plist>

当然,我的代码只对特定情况有用,它会尝试对一个输入为字符串的函数进行模糊测试,并将其与其他字符串进行比较。

server.c:

#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ptrace.h>
#include <mach/mach.h>
#include <stdlib.h>
int main (int argc,char ** argv)
{
    pid_t pid = fork();
    pid_t parentPID, childPID; //maybe it's not really safe, nevermind
    int status;
    if (pid == 0)
    {
        printf ("CHILD %i\n",getpid());
        childPID = getpid();

        FILE * f = fopen(argv[1],"rb");
        if (f == NULL)
        {
            return -2;
            puts ("Cannot open file specified\n");
        }
        int from,to = 0;
        sscanf(argv[2],"%x",&from);
        sscanf(argv[3],"%x",&to);
        if (from >= to)
        {
            puts ("R u out of your mind ? check your range of bytes within the file... \n");
            return -3;
        }
        int fileSize = to - from;
        void * mem = mmap (NULL,fileSize,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE,fileno(f),0);
        if (mem == MAP_FAILED)
        {
            puts ("[!] Cannot allocate memory for file");
            return -4;
        }
        printf ("[-] File mapped to virtual memory : [%p]\n",mem);

        int (*pFunc)(char * str) = (int(*)(char *))(mem+from);

        int ret = pFunc("AAAAA");
        printf ("Returned : %d\n",ret);


    }
    else 
    {
        printf ("PARENT %i\n",getpid());
        parentPID = getpid();

        kern_return_t kret;
        mach_port_t task;
        mach_port_t target_exception_port;
        kret = task_for_pid (mach_task_self(),childPID,&task);
        if (kret != KERN_SUCCESS)
        {
            printf ("task_for_pid() failed with message %s !\n",mach_error_string(kret));
            sleep(100000);
        }

        //save the set of exception ports registered in the process 

        exception_mask_t       saved_masks[EXC_TYPES_COUNT];
        mach_port_t            saved_ports[EXC_TYPES_COUNT];
        exception_behavior_t   saved_behaviors[EXC_TYPES_COUNT];
        thread_state_flavor_t  saved_flavors[EXC_TYPES_COUNT];
        mach_msg_type_number_t saved_exception_types_count;

        task_get_exception_ports(task,
                        EXC_MASK_ALL,
                        saved_masks,
                        &saved_exception_types_count,
                        saved_ports,
                        saved_behaviors,
                        saved_flavors);

        //allocate and authorize a new port 

        mach_port_allocate(mach_task_self(),
                   MACH_PORT_RIGHT_RECEIVE,
                   &task);

        mach_port_insert_right(mach_task_self(),
                       target_exception_port,
                       target_exception_port,
                       MACH_MSG_TYPE_MAKE_SEND);

        //register the exception port with the target process 

        task_set_exception_ports(task,
                         EXC_MASK_ALL,
                         target_exception_port,
                         EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
                         THREAD_STATE_NONE);

        ptrace (PT_ATTACHEXC, childPID,0,0);
    }

    return 0;
}

为什么会输出错误?是 fork 在 OSX 下如何工作的情况吗?那里有什么问题?我不是 osx 如何在下面工作的专家,所以也许我错过了一些东西。感谢您的帮助:)!

多米尼克

【问题讨论】:

  • 1.您是否在 Info.plist 中请求了SecTaskAccess privilege? 2. 你的可执行文件有签名吗? 3.您的进程可能需要以root身份运行,但我不确定这一点,因为访问子进程可能在没有root权限的情况下工作。
  • 是的,我已经完成了所有这些。如果我想要 Finder 的任务,它不会产生错误。正如我认为它不起作用,因为在调用 task_for_pid() 时,父级的 childPID 是未知的。我不知道该怎么做。如何运行一些代码,并在 Mac OS 的一个程序中将其作为调试器进行控制(就像 GDB 一样)?我试图阅读 gdb 源代码,但我很难理解它是如何在那里完成的。
  • 好的,这只是一个实验,但我仍然认为您应该在处理task_for_pid 问题之前检查以下内容。首先,由您的子进程映射执行的二进制文件,您需要确保从from 开始的函数不会调用任何惰性符号或未解析的符号,因为 dyld 不会在这里工作..注意,那个事件printf 是一个懒惰的符号,所以不要使用它。其次,根据您的输出,当父级尝试 task_for_pid 时,可能甚至没有创建子进程。考虑在这两个进程之间进行同步。

标签: c macos debugging fuzzing xnu


【解决方案1】:

您调用pid_t pid = fork(); 因此父进程中子进程的PID 存储在pid 变量中而不是childPID 中。

childPID 变量仅根据您的示例代码在子进程中初始化。

task_for_pid() 调用正在使用 childPID - 基本上您使用的是未初始化的内存。

【讨论】:

  • 非常感谢,这是我对 fork() 工作原理的一点误解。现在我可以绑定到我的子进程:)
【解决方案2】:

除了使用未初始化的pid 的另一个答案中提到的问题之外,看起来您没有将文件作为输入参数传递,是什么导致子进程立即终止并成为僵尸(因为父进程没有等待它的 pid)并且你不能执行 task_for_pid 来终止进程(即使它仍然列在进程表中)

基本上,您需要更仔细地在父子之间进行同步,但作为第一步,为了查明问题,我建议在检查文件是否正确打开之前输入sleep,这样应该可以解决错误在 task_for_pid 中。

【讨论】:

  • @mrdvlpr,没错,但在问题中,显示运行脚本只是简单地调用server而没有任何参数,是什么导致孩子立即下降,因为argv[1]为NULL..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
  • 2011-12-01
  • 1970-01-01
  • 2012-12-31
  • 2020-09-21
  • 1970-01-01
相关资源
最近更新 更多