ptrace - Input/Output 仅 32 位错误(errno 5)
ptrace - Input/Output Error (errno 5) on 32 bits only
我试图在另一个进程中注入一个共享库,我设法让它在 x64 上运行。但是,当我尝试将它用于 32 位时,发生了一些奇怪的事情:由于 Input/Output 错误(errno 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, pid, NULL, &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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_CONT, pid, NULL, NULL) == -1)
{
perror("PTRACE_CONT");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
waitpid(pid, &status, WSTOPPED);
if(ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
handle_ex = (void*)old_regs.eax;
if(ptrace(PTRACE_SETREGS, pid, NULL, &old_regs) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
deallocate_memory(pid, inj_addr, 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_addr, inj_buf, sizeof(inj_buf));
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, pid, NULL, ®s) == -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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
if(ptrace(PTRACE_DETACH, pid, NULL, 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
如果您需要整个项目作为 'minimal' 可重现的示例,请点击此处:https://github.com/rdbo/ptrace-test
PID 正确,我运行正在以 root 用户身份运行,tracer 和 tracee 都是用 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, pid, NULL, &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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
ptrace(PTRACE_CONT, pid, NULL, NULL);
waitpid(pid, &status, WSTOPPED);
ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
deallocate_memory(pid, inj_addr, 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...
编辑:
我只记得我 运行 以 root 身份执行以下命令(可能是这样,但不确定):
echo 0 > /proc/sys/kernel/yama/ptrace_scope
EDIT2:不是,重启后代码仍然有效。此外,我对 GitHub 存储库中的代码进行了一些改进。
我试图在另一个进程中注入一个共享库,我设法让它在 x64 上运行。但是,当我尝试将它用于 32 位时,发生了一些奇怪的事情:由于 Input/Output 错误(errno 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, pid, NULL, &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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_CONT, pid, NULL, NULL) == -1)
{
perror("PTRACE_CONT");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
waitpid(pid, &status, WSTOPPED);
if(ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
handle_ex = (void*)old_regs.eax;
if(ptrace(PTRACE_SETREGS, pid, NULL, &old_regs) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
deallocate_memory(pid, inj_addr, 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_addr, inj_buf, sizeof(inj_buf));
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, pid, NULL, ®s) == -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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
if(ptrace(PTRACE_DETACH, pid, NULL, 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
如果您需要整个项目作为 'minimal' 可重现的示例,请点击此处:https://github.com/rdbo/ptrace-test
PID 正确,我运行正在以 root 用户身份运行,tracer 和 tracee 都是用 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, pid, NULL, &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, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
ptrace(PTRACE_CONT, pid, NULL, NULL);
waitpid(pid, &status, WSTOPPED);
ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
deallocate_memory(pid, inj_addr, 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...
编辑:
我只记得我 运行 以 root 身份执行以下命令(可能是这样,但不确定):
echo 0 > /proc/sys/kernel/yama/ptrace_scope
EDIT2:不是,重启后代码仍然有效。此外,我对 GitHub 存储库中的代码进行了一些改进。