exec()
나는 외부 파일 시스템을 시스템 플래시에 쓰기 전에 테스트할 수 있도록 임베디드 Linux 시스템의 초기화 프로세스를 자체 초기화 프로그램(systemd) 에 강제로 적용하려고 합니다 (그리고 장치가 벽돌이 될 위험이 있습니다). GDB를 사용하면 명령을 실행 gdb --pid=1
한 다음 해당 셸 유형에서 실행할 수 있지만 call execl("/lib/systemd/systemd", "systemd", 0)
(필요한 방식으로 정확히 작동합니다) 시스템의 플래시 메모리에 GDB를 넣을 공간이 충분하지 않습니다.
ptrace()
GDB를 호출하는 명령이 정확히 무엇을 사용하는지 알고 싶습니다. call
이를 내 자신의 간단한 C 프로그램에서 구현할 수 있습니다.
strace
GDB가 사용하는 호출이 무엇인지 알아내려고 노력 중이지만 ptrace()
결과 파일 길이는 172,031줄입니다. 소스 코드도 살펴봤지만 원하는 파일을 찾기에는 파일이 너무 많습니다.
장치는 Linux 커널 버전 3.10.0을 실행하고 있으며 구성은 여기에서 사용할 수 있습니다.https://pastebin.com/rk0Zux62
답변1
다음은 이를 수행하는 C 프로그램입니다. 다음과 같은 몇 가지 알려진 문제에 유의하세요.
- 아마도 엄격한 별칭 위반 대신 memcpy를 사용해야 할 것입니다.
- 이전 피추적자의 환경 변수 대신 자신의 환경 변수를 사용하세요.
- 피추적자가 시스템 호출을 하지 않는다면 이는 결코 아무 일도 하지 않을 것입니다.
- 피추적자가 언제 정지했는지 확인하지 않고 그것이 실제로 시스템 호출 정지인지 신호 정지 같은 것이 아닌지 확인하지 않음
- IP는 syscall-enter-stop 대신 syscall-exit-stop에서 재설정되어야 합니다.
- 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, ®s)) {
perror("ptrace(PTRACE_GETREGS, ...)");
return 1;
}
/* End up back on the syscall instruction so we can use it again */
regs.ARM_pc -= (thumb_mode(®s) ? 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, ®s)) {
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, ®s)) {
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, ®s)) {
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, ®s)) {
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 커널과 wheezy userland를 사용하여 개발하고 테스트했습니다.https://people.debian.org/~aurel32/qemu/armel/