ptrace-仅32位输入/输出错误errno 5

问题描述

我试图在另一个进程中注入共享库,但设法在x64上运行。但是,当我尝试将其用于32位时,发生了一些奇怪的事情:由于输入/输出错误错误号5),ptrace无法正确执行。我不知道该怎么办,因为相同的代码适用于x64。 然后,我尝试使用一个名为test_ptrace函数制作一个较小的示例。出人意料的是,该错误并未发生,尽管它实际上是在做同一件事(在目标进程上分配内存,注入有效负载,设置寄存器以匹配有效负载,运行有效负载)。当我看到错误没有发生时,我再次尝试使用名为load_library函数向共享库注入ptrace。但不幸的是,错误再次出现。

//this is the function that is NOT working,'load_library'
void* load_library(pid_t pid,std::string path,int mode)
{
    int status;
    struct user_regs_struct old_regs,regs;
    void* dlopen_ex = (void*)0xf7c29700; //I disabled ASLR,so this address does not change
    void* handle_ex = (void*)-1;

    unsigned char inj_buf[] =
    {
        0x51,//push ecx
        0x53,//push ebx
        0xFF,0xD0,//call eax
        0xCC,//int3 (SIGTRAP)
    };

    size_t path_size = path.size();
    size_t inj_size  = sizeof(inj_buf) + path_size;
    void*  inj_addr  = allocate_memory(pid,inj_size,PROT_EXEC | PROT_READ | PROT_WRITE);
    void*  path_addr = (void*)((uintptr_t)inj_addr + sizeof(inj_buf));
    write_memory(pid,inj_addr,(void*)inj_buf,sizeof(inj_buf));
    write_memory(pid,path_addr,(void*)path.c_str(),path_size);

    if(ptrace(PTRACE_ATTACH,pid,NULL,NULL))
    {
        perror("PTRACE_ATTACH");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }
    wait(&status);
    if(ptrace(PTRACE_GETREGS,&old_regs) == -1)
    {
        perror("PTRACE_GETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }

    regs.eax = (unsigned long)dlopen_ex;
    regs.ebx = (unsigned long)path_addr;
    regs.ecx = (unsigned long)mode;
    regs.eip = (unsigned long)inj_addr;

    if(ptrace(PTRACE_SETREGS,&regs) == -1)
    {
        perror("PTRACE_SETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }
    
    if(ptrace(PTRACE_CONT,NULL) == -1)
    {
        perror("PTRACE_CONT");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }

    waitpid(pid,&status,WSTOPPED);
    if(ptrace(PTRACE_GETREGS,&regs) == -1)
    {
        perror("PTRACE_GETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }

    handle_ex = (void*)old_regs.eax;

    if(ptrace(PTRACE_SETREGS,&old_regs) == -1)
    {
        perror("PTRACE_SETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }

    if(ptrace(PTRACE_DETACH,NULL) == -1)
    {
        perror("PTRACE_DETACH");
        std::cout << "Errno: " << errno << std::endl;
        return handle_ex;
    }

    deallocate_memory(pid,inj_size);

    return handle_ex;
}
//this one,though,is working,but it is very similar to the function 
//above (except it doesn't restore the execution,but the code of the 
//other function doesn't even get there anyway.
void test_ptrace(pid_t pid)
{
    int status;
    struct user_regs_struct regs;
    unsigned char inj_buf[] =
    {
        0xCD,0x80,//int80 (syscall)
        0xCC,//int3  (SIGTRAP)
    };

    void* inj_addr = allocate_memory(pid,sizeof(inj_buf),PROT_EXEC | PROT_READ | PROT_WRITE);
    write_memory(pid,inj_buf,sizeof(inj_buf));

    std::cout << "--ptrace test started--" << std::endl;

    if(ptrace(PTRACE_ATTACH,NULL) == -1)
    {
        perror("PTRACE_ATTACH");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    wait(&status);

    if(ptrace(PTRACE_GETREGS,&regs) == -1)
    {
        perror("PTRACE_GETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    regs.eax = __NR_exit;
    regs.ebx = 222;
    regs.eip = (unsigned long)inj_addr;

    if(ptrace(PTRACE_SETREGS,&regs) == -1)
    {
        perror("PTRACE_SETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    if(ptrace(PTRACE_DETACH,NULL) == -1)
    {
        perror("PTRACE_DETACH");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    std::cout << "--ptrace test ended--" << std::endl;
}

程序入口:

int main()
{
    pid_t pid = get_process_id("target");
    std::cout << "PID: " << pid << std::endl;
    std::string lib_path = "<my_path>/ptrace-test/libtest.so";
    load_library(pid,lib_path,RTLD_LAZY);
    return 0;
}

输出

PID: 2383
PTRACE_SETREGS: Input/output error
Errno: 5

如果您需要整个项目作为“最小的”可复制示例,请转到:https://github.com/rdbo/ptrace-test

PID是正确的,我以root身份运行,跟踪器和被跟踪都使用G ++在32位上编译。运行最新的manjaro。有什么想法吗?

解决方法

我修复了它。不过,不要问我如何不知道。我遵循完全相同的逻辑,突然间它奏效了。我使用了有效的test_ptrace,并不断将load_library代码逐行放置在代码上,以查看可能引起问题的原因。原来,我已将其修复,但仍然不知道它是什么。无论如何,这是代码(有点混乱,因为我没想到它会起作用):

void load_library(pid_t pid,std::string lib_path)
{
    int status;
    struct user_regs_struct old_regs,regs;
    unsigned char inj_buf[] =
    {
        0x51,//push ecx
        0x53,//push ebx
        0xFF,0xD0,//call eax
        0xCC,//int3 (SIGTRAP)
    };

    size_t inj_size = sizeof(inj_buf) + lib_path.size();
    void* inj_addr = allocate_memory(pid,inj_size,PROT_EXEC | PROT_READ | PROT_WRITE);
    void* path_addr = (void*)((uintptr_t)inj_addr + sizeof(inj_buf));
    write_memory(pid,inj_addr,inj_buf,sizeof(inj_buf));
    write_memory(pid,path_addr,(void*)lib_path.c_str(),lib_path.size());

    std::cout << "--ptrace test started--" << std::endl;

    if(ptrace(PTRACE_ATTACH,pid,NULL,NULL) == -1)
    {
        perror("PTRACE_ATTACH");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    wait(&status);

    if(ptrace(PTRACE_GETREGS,&old_regs) == -1)
    {
        perror("PTRACE_GETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    regs = old_regs;

    long dlopen_ex = 0xf7c28700;

    regs.eax = dlopen_ex;
    regs.ebx = (long)path_addr;
    regs.ecx = RTLD_LAZY;
    regs.eip = (unsigned long)inj_addr;

    if(ptrace(PTRACE_SETREGS,&regs) == -1)
    {
        perror("PTRACE_SETREGS");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    ptrace(PTRACE_CONT,NULL);
    waitpid(pid,&status,WSTOPPED);
    ptrace(PTRACE_SETREGS,&old_regs);

    if(ptrace(PTRACE_DETACH,NULL) == -1)
    {
        perror("PTRACE_DETACH");
        std::cout << "Errno: " << errno << std::endl;
        return;
    }

    deallocate_memory(pid,inj_size);

    std::cout << "--ptrace test ended--" << std::endl;
}

输出:

PID: 24615
--ptrace test started--
--ptrace test ended--

目标流程:

PID: 24615
dlopen: 0xf7c28700
Waiting...
Injected!
Waiting...
Waiting...
Waiting...
Waiting...

编辑: 我只记得我以超级用户身份运行了以下命令(可以,但是不确定): echo 0 > /proc/sys/kernel/yama/ptrace_scope
EDIT2:不是,重新启动后代码仍在工作。此外,我在GitHub存储库上对代码进行了一些改进。