/* 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; }