Lendo dados de syscall via ptrace

Salve, salve! Dessa vez, começo minha aventura pelos mares do ptrace! Vejo como uma boa área para adquirir algum aprendizado no que diz respeito a alteração/extração de informação de um processo em execução, entre outros tipos de utilidade. Tentarei reportar de forma didática o que eu descobrir sobre o assunto, e especificamente, com exemplos usando arquitetura x86_64 (o que é raro de ver por ai).

Para quem não conhece, ptrace é o mecanismo (é uma syscall) provido pelo Kernel para fazer com que processo pai consiga observar e controlar a execução de outro processo.

Venho seguindo o artigo Playing with ptrace, Part I [1], o qual possui outras partes em que vai se aprofundando no assunto. Creio que ele irá suprir minhas dúvidas, e tenha as informações complementares à manpage.

Neste primeiro post mostrarei como é possível ler argumentos de uma syscall usando o ptrace e fazendo os seguintes passos: Criar processo filho -> Marcá-lo que haverá trace -> Checar por uma determinada syscall -> Ler os registradores.

Todo o uso do ptrace dar-se por meio da função ptrace():

long ptrace(enum __ptrace_request request, pid_t pid,
                   void *addr, void *data);

Onde request pode ser um dos possíveis valores: PTRACE_TRACEME, PTRACE_PEEKTEXT, PTRACE_PEEKDATA, PTRACE_PEEKUSER, PTRACE_POKETEXT, PTRACE_POKEDATA, PTRACE_POKEUSER, PTRACE_GETREGS, PTRACE_GETFPREGS, PTRACE_SETREGS, PTRACE_SETFPREGS, PTRACE_CONT, PTRACE_SYSCALL, PTRACE_SINGLESTEP, PTRACE_DETACH.

A seguir veremos o uso de algum deles neste primeiro código de exemplo, com os devidos comentários no código.

Código do programa que será executado pelo processo filho:

#include <stdio.h>
 
int main(int argc, char **argv) {
	printf("foobar!!\n");
	return 0;
}

Código do programa principal:

#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <stdlib.h>
#include <string.h>
 
/* Obtido em /usr/include/asm/ptrace-abi.h */
#define RAX 80
#define RSI 104
 
const int long_size = sizeof(long);
 
void getdata(pid_t child, long addr, char *str, int len) {
	char *laddr = str;
	int i = 0, j = len / long_size;
	int is_exact = len % long_size;
	long val;
 
	while (i <= j) {
		if (i == j && is_exact == 0) {
			break;
		}
 
		/* Lendo dados do processo filho */
		val = ptrace(PTRACE_PEEKDATA, child, addr + i * long_size, NULL);
 
		memcpy(laddr, &val, i == j ? (len % long_size) : long_size);		
		++i;
		laddr += long_size;
	}
 
	str[len] = '\0';
}
 
int main(int argc, char **argv) {
	pid_t child;
	long orig_rax, eax;
	struct user_regs_struct regs;
	int status, insyscall = 0;
 
	child = fork();
 
	if (child == 0) {
		/* Diz ao kernel que o processo passará a ser "traceado" */
		ptrace(PTRACE_TRACEME, 0, NULL, NULL);
		execl("./foo", NULL, NULL);
	} else {
		while(1) {
			/* Aguarda pela execução da syscall sys_execve */
			wait(&status);
 
			if (WIFEXITED(status)) {
				break;
			}
			/* Obtendo os valores dos registradores usados na syscall */
			ptrace(PTRACE_GETREGS, child, 0, &regs);
 
			if (regs.orig_rax == __NR_write) {
				if (insyscall == 0) {
					long val;
 
					/* Endereço da string como argumento da sys_write() */
					val = ptrace(PTRACE_PEEKUSER, child, RSI, NULL);
 
					char *str = malloc(regs.rbx + 1 * regs.rdi);
 
					getdata(child, val, str, regs.rbx);					
					printf("arg: '%s'\n", str);
 
					free(str);
 
					insyscall = 1;
				} else {
					/* Obtem o valor retornado pela syscall */
					eax = ptrace(PTRACE_PEEKUSER, child, RAX, NULL);
					printf("Write returned with %ld\n", eax);
					insyscall = 0;
				}				
			}
			/* Indicamos que o kernel deve parar o processo filho a cada 
			 * syscall ou exit */
			ptrace(PTRACE_SYSCALL, child, NULL, NULL);
		}
	}
	return 0;
}

Observação: Repare que o primeiro programa é executado neste último código através de "./foo".

Outra parte a se observar no código, é a leitura da string do argumento via variável `val' (do tipo long) na função getdata() usando memcpy().

Referências

[1] - http://www.linuxjournal.com/article/6100

cara, explique as constantes

cara, explique as constantes e os parametros a função, isso facilita muito, agente tem que descobrir pra que serve cada um, e aumenta a area do código fonte também...

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options