Contents

Introdução ao depurador GNU no Linux: Um curso rápido

A depuração é uma competência indispensável para programadores e investigadores de segurança. Ter uma forte compreensão da depuração permite-lhe compreender um executável a um nível inferior e detetar quaisquer erros ocultos.

O GNU Debugger, normalmente referido como GDB, é um utilitário de depuração duradouro que tem sido utilizado e confiado pelos programadores ao longo do tempo. Nesta secção, iremos explorar a sua aplicação no contexto dos sistemas Linux.

Preparando programas de amostra

Para se aprofundar nos recursos do GDB, é necessário testá-lo usando um executável cujo código-fonte e símbolos de depuração estejam acessíveis para exame. Como exemplo ilustrativo, vamos executar o GDB em dois programas distintos - uma aplicação de verificação de chaves e um programa multi-threaded que exibe mensagens na tela - um desenvolvido em C e compilado utilizando o Compilador GNU C, enquanto o outro não possui seu código fonte.

O utilizador tem a opção de utilizar qualquer compilador C alternativo, desde que se abstenha de remover os metadados do ficheiro executável, que contêm informações sobre a origem do programa e as modificações efectuadas durante a compilação.

/pt/images/compiling-and-executing-the-sample-programs.jpg

Para utilizar o GDB de forma eficaz, é muito provável que o utilize nas suas próprias aplicações. Consequentemente, é essencial garantir que esses programas sejam compilados usando o sinalizador -g com gcc , que permite a inclusão de símbolos de depuração.

De facto, quando os símbolos de depuração estão ausentes e o binário foi significativamente minimizado, é necessário investigar o código dissemelhante da aplicação. Tal esforço requer uma compreensão proficiente da linguagem assembly e uma compreensão intrincada dos mecanismos de alocação de memória no sistema operativo Linux, bem como a interpretação dos dados armazenados na pilha e nos registos.

Executando um programa no GDB

/pt/images/executing-a-program-in-gdb.jpg

Como alternativa, você também pode optar por iniciar o GNU Debugger digitando “gdb” seguido dos argumentos de linha de comando desejados, o que o levará subsequentemente à entrada de prompt para o binário de destino específico que precisa de depuração. Uma vez carregado, pode continuar a executar o programa introduzindo o comando “run” ou simplesmente usar atalhos de teclado como “r”.

Para que um programa funcione corretamente quando requer argumentos da linha de comandos, é essencial anexá-los a seguir ao título do programa aquando da invocação.A estrutura gramatical utilizada pelo GNU Debugger (GDB) para carregar e executar uma aplicação juntamente com os seus argumentos pode ser descrita da seguinte forma:

 gdb <program>
run <args> 

Ou:

 gdb
file <program>
run <args> 

Definindo pontos de interrupção com o GDB

/pt/images/setting-and-deleting-breakpoints.jpg

Os pontos de interrupção, que são interrupções intencionais no fluxo de execução durante a depuração, servem como pontos de parada predeterminados dentro do código. Ao colocar estrategicamente estes marcadores, os programadores podem fazer uma pausa na progressão do seu programa em momentos específicos e examinar meticulosamente os efeitos nos dados e nas variáveis à medida que cada fase se desenrola.

Ao utilizar o GNU Debugger (GDB) para depurar um aplicativo de software que incorpora símbolos de depuração, pode-se optar por dois métodos de definição de pontos de interrupção. Eles incluem a especificação de um ponto de interrupção usando o título de uma função específica ou a designação de um ponto de interrupção com base em um número de linha específico dentro do código. A sintaxe correspondente é a seguinte:

 break main
break 47 

Para acessar a lista completa de pontos de interrupção que estão atualmente ativos durante esta sessão de depuração, digite o seguinte comando:

 info breakpoints 

Para remover um ponto de interrupção específico ou todos os pontos de interrupção actuais no seu código, pode utilizar o comando “delete " (se estiver a remover um) ou simplesmente “delete” (para os eliminar a todos).

 delete 2
delete 3-5 

O GDB fornece a habilidade de estabelecer pontos de parada condicionais, que requerem que uma condição específica seja satisfeita para que o programa pause durante sua execução. Esta condição pode envolver alterações no valor de uma variável, uma chamada de função improdutiva, ou qualquer outro critério de sua preferência. A seguir é apresentada a sintaxe utilizada para designar pontos de interrupção condicionais:

 break <location> if n == 2 

Se desejar retomar o funcionamento do programa ao encontrar um ponto de interrupção, introduza o comando “continue”.

 continue 

Percorrendo o código

Examinar a execução de programas de computador linha por linha é essencial para compreender o seu funcionamento na informação. Ao percorrer cuidadosamente várias funções dentro do software e escrutinar o estado dos dados, é possível obter uma visão mais profunda da forma como o sistema executa os princípios codificados no seu código fonte.

O software permite uma análise abrangente das falhas do sistema, identificando a sua origem, ao mesmo tempo que proporciona um exame aprofundado do funcionamento do programa através do controlo preciso das linhas individuais de código. O processo de execução de cada linha sequencialmente é possível em três métodos distintos dentro da ferramenta de depuração conhecida como GDB.

A diretiva acima mencionada instrui o GNU Debugger (GDB) a prosseguir com as linhas subsequentes do respetivo ficheiro fonte, permitindo assim uma passagem abrangente da base de código numa base individual.

O comando “next” no REPL do Pycharm permite executar as linhas de código subsequentes dentro da função atualmente ativa, após o que a execução cessa. Ao contrário do comando “step”, que opera em instruções ou instruções individuais, “next” considera uma função inteira como um bloco contínuo de código, permitindo assim que os utilizadores executem várias linhas sem interrupção.

O comando finish conclui efetivamente a execução de quaisquer blocos de código pendentes que permaneçam na função atual, seguido de uma interrupção do procedimento.

Examinando variáveis

/pt/images/using-the-display-command.jpg

Para analisar a alteração da lógica do programa examinando os valores das variáveis durante a execução de um programa usando o GDB, pode-se utilizar a seguinte sintaxe para exibir o valor atual de variáveis específicas no ambiente de depuração:

 print <variable> 

Para exibir as modificações feitas no valor de uma determinada variável a cada iteração dentro de um loop, pode-se empregar o comando “display”. Esta abordagem pode ser particularmente benéfica para monitorizar e documentar o progresso do valor da referida variável durante o seu processo iterativo.

 display <variable> 

Definir pontos de observação

Os pontos de observação e os pontos de interrupção condicionais estão inter-relacionados na medida em que ambos reagem a alterações num programa. Especificamente, os watchpoints servem para monitorizar as modificações nos dados que ocorrem no código fonte. Como ilustração, pode-se desejar que o software faça uma pausa quando o conteúdo de uma determinada variável sofre alguma alteração. O processo de implementação desta funcionalidade utilizando a GDB é detalhado a seguir:

 watch <variable_name> 

Depuracao especifica de thread com GDB

/pt/images/thread-specific-debugging-in-gdb.jpg

Utilizando o depurador GDB, e possivel realizar operacoes de depuracao em threads individuais dentro de aplicativos multi-threaded. Como um exemplo ilustrativo, considere um programa C conciso que emprega vários threads para gerar mensagens impressas.

Para obter informações sobre as threads activas na sua aplicação, utilize o comando “info” seguido de uma linha em branco.

 info threads 

Para aceder e interagir com uma determinada thread na lista, pode utilizar a sua posição numérica ou índice dentro do conjunto de opções disponíveis.Isto permite a seleção e manipulação do segmento desejado com base no identificador que lhe foi atribuído.

 thread 2 

Ao escolher uma thread específica, é possível percorrer progressivamente a sua sequência de execução utilizando os comandos de passo “step”, “next” e “finish”, conforme ilustrado anteriormente.

Depuração Remota com GDB

/pt/images/connecting-to-remote-gbdserver.jpg

Para depurar remotamente aplicações de software que estão alojadas noutro dispositivo, é necessário configurar o gdbserver no computador de destino. Este processo pode ser realizado utilizando a ferramenta de gestão de pacotes pré-instalada no seu sistema operativo ou empregando quaisquer gestores de pacotes adicionais que tenham sido instalados no seu sistema.

Para instalar o gdbserver num sistema que utiliza Ubuntu ou Debian como base, pode empregar-se a Ferramenta de Pacotes Avançados (APT) para este fim.

 sudo apt install gdbserver 

Após a execução do processo de instalação, navegue até ao diretório que contém o binário e execute um comando específico dentro dessa localização de modo a iniciar o funcionamento do programa gdbserver.

 gdbserver <ip>:<port> <binary> 

Espera-se que a aplicação “gdbserver” apresente uma mensagem indicando que foi iniciada e que está ativamente à escuta de ligações de entrada na porta especificada. Subsequentemente, noutro dispositivo, inicie uma instância do software “GDB” e estabeleça uma ligação com o servidor distante, utilizando o comando “target” seguido da designação específica para o objeto de consulta pretendido.

 target remote <server_ip>:<port> 

Escrevendo scripts GDB para automatizar a depuração

/pt/images/gdb-scripting-example.jpg

A utilização de scripts GDB permite que os desenvolvedores executem comandos GDB pré-determinados repetidamente com facilidade. Consequentemente, isso agiliza o processo de depuração, eliminando a necessidade de tarefas repetitivas, como definir pontos de interrupção, navegar pela execução do código e imprimir dados variáveis. Ao utilizar estes scripts, os programadores podem automatizar uma série de acções que de outra forma seriam entediantes, aumentando assim a sua produtividade durante a depuração de partes específicas do código.

Aqui está um exemplo:

 set logging enabled on
set logging file sample.out
break main
  command 1
  backtrace
  print N
  continue
end
quit

No trecho de código fornecido, é instruído que a ferramenta de depuração GDB deve ativar sua funcionalidade de registro e armazenar o registro resultante em um arquivo chamado “sample.out”. Além disso, é designado um ponto de interrupção na função principal do programa.

Para executar o ponto de interrupção um, que está situado no início da função ‘main’, siga estes passos utilizando o GNU Debugger (GDB): Em primeiro lugar, inicie um backtrace introduzindo o comando ‘backtrace’.Em segundo lugar, mostra o valor da variável “N” com a instrução “print N”. Em terceiro lugar, prossiga com a execução do programa escrevendo “continue”. O depurador conduzirá inicialmente um backtrace para determinar o estado atual do fluxo do programa. Posteriormente, exibirá o valor da variável ‘N’. Depois disso, retoma o processo de execução. Por fim, uma vez que todas as informações relevantes tenham sido exibidas, o GDB encerrará graciosamente sua operação.

Para executar este script, use:

 gdb -x <script> <binary> 

Agora você sabe como depurar seus programas com o GDB!

A depuração é uma proficiência crítica, e dominar a utilização do GDB para fins de depuração aumenta significativamente a versatilidade de um desenvolvedor. As várias funcionalidades fornecidas pelo GDB, como a navegação passo a passo pelo código, a colocação de pontos de interrupção, a depuração de threads direcionadas e outras, tornam-no um instrumento potente ao examinar arquivos binários no ambiente do sistema operacional Linux.

Para quem procura analisar e resolver problemas de programas de software no sistema operativo Windows, explorar mais conhecimentos sobre o Windbg, uma ferramenta de depuração integrada nativamente para o Windows, pode ser considerada uma opção que vale a pena considerar.