/* OpenIDA - Outil d'analyse de fichiers binaires
* ptrace.c - débogage à l'aide de ptrace()
*
* Copyright (C) 2008 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* OpenIDA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* OpenIDA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "ptrace.h"
#include
#include
#include
#include
#include "options.h"
#include "../debugger-int.h"
#include "../../panel/log.h"
/** Partie ptrace() **/
#include
#include
//#include
#include /* For SYS_write etc */
#include
/** Partie ptrace() **/
#define _(str) str
/* Débogueur utilisant ptrace() (instance) */
struct _GPtraceDebugger
{
GBinaryDebugger parent; /* A laisser en premier */
GCond *cond; /* Poursuite du déroulement */
GMutex *mutex; /* Accès à la condition */
ptrace_options *options; /* Configuration du débogage */
pid_t child; /* Processus suivi lancé */
gboolean run_again; /* Reprise du débogage */
};
/* Débogueur utilisant ptrace() (classe) */
struct _GPtraceDebuggerClass
{
GBinaryDebuggerClass parent; /* A laisser en premier */
};
/* Met en marche le débogueur utilisant ptrace(). */
bool g_ptrace_debugger_run(GPtraceDebugger *);
/* Remet en marche le débogueur utilisant ptrace(). */
bool g_ptrace_debugger_resume(GPtraceDebugger *);
/* Tue le débogueur utilisant ptrace(). */
bool g_ptrace_debugger_kill(GPtraceDebugger *);
/* Procède à un débogage via ptrace(). */
void *ptrace_thread(GPtraceDebugger *);
/* Fournit la valeur des registres de l'architecture. */
register_value *get_register_values_using_ptrace_debugger(GPtraceDebugger *, size_t *);
/* Initialise la classe du débogueur utilisant ptrace(). */
static void g_ptrace_debugger_class_init(GPtraceDebuggerClass *);
/* Procède à l'initialisation du débogueur utilisant ptrace(). */
static void g_ptrace_debugger_init(GPtraceDebugger *);
/* Indique le type définit par la GLib pour le débogueur ptrace(). */
G_DEFINE_TYPE(GPtraceDebugger, g_ptrace_debugger, G_TYPE_BINARY_DEBUGGER);
/******************************************************************************
* *
* Paramètres : klass = classe de débogueur à initialiser. *
* *
* Description : Initialise la classe du débogueur utilisant ptrace(). *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_ptrace_debugger_class_init(GPtraceDebuggerClass *klass)
{
}
/******************************************************************************
* *
* Paramètres : debugger = instance de débogueur à préparer. *
* *
* Description : Procède à l'initialisation du débogueur utilisant ptrace(). *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_ptrace_debugger_init(GPtraceDebugger *debugger)
{
GBinaryDebugger *parent; /* Instance parente */
parent = G_BINARY_DEBUGGER(debugger);
parent->run = (basic_debugger_fc)g_ptrace_debugger_run;
parent->resume = (resume_debugger_fc)g_ptrace_debugger_resume;
parent->kill = (basic_debugger_fc)g_ptrace_debugger_kill;
parent->get_reg_values = (get_register_values_fc)get_register_values_using_ptrace_debugger;
debugger->cond = g_cond_new();
debugger->mutex = g_mutex_new();
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à lancer. *
* *
* Description : Met en marche le débogueur utilisant ptrace(). *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_ptrace_debugger_run(GPtraceDebugger *debugger)
{
GError *error; /* Bilan de création de thread */
if (debugger->options == NULL)
debugger->options = create_ptrace_options_from_binary(G_BINARY_DEBUGGER(debugger)->binary);
if (debugger->options == NULL)
return false;
if (!g_thread_create((GThreadFunc)ptrace_thread, debugger, FALSE, &error))
{
printf ("Failed to create the thread: %s\n", error->message);
}
/*
printf("Start Debugger with bin :: %p\n", G_BINARY_DEBUGGER_GET_IFACE(debugger)->binary);
g_signal_emit_by_name(debugger, "debugger-stopped", (uint64_t)0xdeadbeaf);
*/
return true;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* *
* Description : Remet en marche le débogueur utilisant ptrace(). *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_ptrace_debugger_resume(GPtraceDebugger *debugger)
{
g_mutex_lock(debugger->mutex);
debugger->run_again = TRUE;
g_cond_signal(debugger->cond);
g_mutex_unlock(debugger->mutex);
return true;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à relancer. *
* *
* Description : Tue le débogueur utilisant ptrace(). *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_ptrace_debugger_kill(GPtraceDebugger *debugger)
{
int ret; /* Bilan de l'appel système */
ret = kill(debugger->child, SIGKILL);
if (ret != 0) perror("kill");
debugger->child = 0;
g_mutex_lock(debugger->mutex);
debugger->run_again = TRUE;
g_cond_signal(debugger->cond);
g_mutex_unlock(debugger->mutex);
return true;
}
/******************************************************************************
* *
* Paramètres : debugger = encadrement associée à l'opération. *
* *
* Description : Procède à un débogage via ptrace(). *
* *
* Retour : ??? *
* *
* Remarques : - *
* *
******************************************************************************/
void *ptrace_thread(GPtraceDebugger *debugger)
{
long last_eip; /* Ancien point d'arrêt */
long cur_eip; /* Point d'arrêt courant */
long orig_eax;
int status;
pid_t pid;
bool first_run; /* Premier passage ? */
debugger->child = fork();
switch (debugger->child)
{
case -1:
perror("fork");
exit(-1);
break;
case 0:
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
run_ptrace_options_process(debugger->options);
_exit(-1);
break;
default:
gdk_threads_enter();
log_variadic_message(LMT_PROCESS, _("Starting to debug %s..."),
g_openida_binary_get_filename(G_BINARY_DEBUGGER(debugger)->binary));
gdk_flush ();
gdk_threads_leave();
first_run = true;
while(1)
{
pid = waitpid(debugger->child, &status, 0/*WNOHANG*/);
//wait(&status);
printf("Status :: %d\n", WIFEXITED(status));
if(WIFEXITED(status))
break;
orig_eax = ptrace(PTRACE_PEEKUSER,
debugger->child, 4 * ORIG_EAX, NULL);
if (orig_eax == -1)
{
//printf("errno :: %d vs %d\n", errno, ESRCH);
perror("ptrace()");
}
/* get GTK thread lock */
gdk_threads_enter();
//gtk_text_buffer_insert_at_cursor (info->buffer, "Thread waiting for resume...\n", -1);
//gdk_flush ();
/* release GTK thread lock */
//gdk_threads_leave();
/* Notification du point d'arrêt */
cur_eip = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EIP, NULL);
if (first_run)
{
last_eip = cur_eip;
first_run = false;
}
g_signal_emit_by_name(debugger, "debugger-stopped",
(uint64_t)last_eip, (uint64_t)cur_eip);
last_eip = cur_eip;
/* release GTK thread lock */
gdk_flush ();
gdk_threads_leave();
g_mutex_lock(debugger->mutex);
while (!debugger->run_again)
g_cond_wait(debugger->cond, debugger->mutex);
debugger->run_again = FALSE;
g_mutex_unlock(debugger->mutex);
if (debugger->child == 0) break;
ptrace(PTRACE_SYSCALL, debugger->child, NULL, NULL);
}
log_variadic_message(LMT_PROCESS, _("Finished to debug %s..."),
g_openida_binary_get_filename(G_BINARY_DEBUGGER(debugger)->binary));
break;
}
return NULL;
}
/******************************************************************************
* *
* Paramètres : debugger = débogueur à utiliser. *
* count = nombre de transmissions effetuées. *
* *
* Description : Fournit la valeur des registres de l'architecture. *
* *
* Retour : Tableau de valeurs transmises à libérer de la mémoire / NULL.*
* *
* Remarques : - *
* *
******************************************************************************/
register_value *get_register_values_using_ptrace_debugger(GPtraceDebugger *debugger, size_t *count)
{
register_value *result; /* Liste de valeurs renvoyées */
long ret; /* Valeur de registre */
*count = 9;
result = (register_value *)calloc(*count, sizeof(register_value));
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EAX, NULL);
result[0].name = "eax";
result[0].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EBX, NULL);
result[1].name = "ebx";
result[1].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * ECX, NULL);
result[2].name = "ecx";
result[2].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EDX, NULL);
result[3].name = "edx";
result[3].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * ESI, NULL);
result[4].name = "esi";
result[4].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EDI, NULL);
result[5].name = "edi";
result[5].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EBP, NULL);
result[6].name = "ebp";
result[6].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * UESP, NULL);
result[7].name = "esp";
result[7].value = ret;
ret = ptrace(PTRACE_PEEKUSER, debugger->child, 4 * EIP, NULL);
result[8].name = "eip";
result[8].value = ret;
return result;
}