Melhore a qualidade do código e evite resultados inesperados aprendendo como usar o GNU Debugger para revelar bugs indesejados no seu código.

A depuração é uma habilidade indispensável para programadores e pesquisadores de segurança. Ter um bom domínio de depuração permite que você entenda um executável em um nível inferior e detecte quaisquer erros ocultos.

O depurador GNU ou GDB é uma ferramenta de depuração atemporal na qual os programadores confiam há anos. Veja como usar o GDB no Linux.

Preparando programas de amostra

Para explorar os recursos do GDB você precisará de um executável para experimentar. Para demonstração, você executará o GDB em um programa de verificação de chaves uma vez com código-fonte e símbolos de depuração disponíveis, uma vez sem código-fonte e em um programa multithread simples que imprime mensagens na tela, ambos escritos em C e compilados com GCC (GNU C Compilador).

Você pode use qualquer outro compilador C mas certifique-se de não remover o binário.

Você provavelmente executará o GDB em seus próprios programas. Portanto, certifique-se de compilá-los com o

-g sinalizador com gcc para ativar símbolos de depuração.

Sem os símbolos de depuração presentes e com um binário bastante reduzido, você terá que depurar a desmontagem do programa. Isso exigirá que você tenha um bom domínio da linguagem assembly e como funciona a alocação de memória no Linux para entender os dados na pilha e nos registros.

Executando um programa no GDB

Você executa um programa no GDB de duas maneiras. Ou digite gdb , e assim que carregar, digite correr. Ou inicie o gdb e depois use o arquivo comando, carregue o binário no gdb e execute-o com o correr comando.

Se o seu programa exigir argumentos de linha de comando para funcionar corretamente, adicione os argumentos após o nome do programa. Aqui está a sintaxe para carregar o programa no GDB e executá-lo com argumentos:

gdb 
run

Ou:

gdb
file
run

Configurando pontos de interrupção com GDB

Os pontos de interrupção na depuração são paradas bruscas definidas manualmente no código que interrompem o fluxo de execução quando o programa atinge um ponto de interrupção. Definir pontos de interrupção permite percorrer o código e inspecionar como cada estágio de execução afeta dados e variáveis.

No GDB, ao depurar um programa com símbolos de depuração, você pode definir um ponto de interrupção pelo nome da função ou definir um ponto de interrupção com base no número da linha. Aqui está a sintaxe:

break main
break 47

Para visualizar todos os pontos de interrupção na sessão de depuração atual, digite:

info breakpoints

Para excluir um ponto de interrupção específico ou vários pontos de interrupção, digite:

delete 2
delete 3-5

O GDB também permite definir pontos de interrupção condicionais, o que significa que o programa só será interrompido se uma condição específica for satisfeita durante a execução. Pode ser a mudança no valor de uma variável ou uma chamada de função malsucedida ou qualquer coisa que você queira. Aqui está a sintaxe para definir pontos de interrupção condicionais:

break  if n == 2

Se você deseja continuar a execução do programa após atingir um ponto de interrupção, digite o continuar comando:

continue

Percorrendo o código

Percorrer o código é crucial para entender como o programa está lidando com os dados. Ao percorrer várias funções em seu programa e examinar o estado dos dados, você pode compreender melhor como o programa está implementando a lógica que você escreveu no código.

Ele também ajuda a detectar a raiz das falhas e estudar o comportamento do programa com precisão cirúrgica, pois você pode percorrer cada linha de código conforme desejar. Você pode percorrer o código de três maneiras principais no GDB:

  1. etapa: Este comando diz ao GDB para passar para a próxima linha do arquivo de origem. Isso permite que você percorra essencialmente o comprimento do código-fonte linha por linha.
  2. próximo: Este comando executa a próxima linha do código-fonte dentro da função atual e então para. próximo trata uma função como uma única linha, portanto, se você usar next antes de uma chamada de função, ele a tratará como uma única linha e passará por cima dela, ao contrário do etapa comando.
  3. terminar: O comando finish executa todas as linhas restantes dentro da função atual e então para.

Examinando Variáveis

À medida que avança no código, você desejaria examinar o valor das variáveis ​​para ver como a lógica do programa as está alterando. Aqui está a sintaxe para visualizar o valor das variáveis ​​no GDB:

print 

Caso queira imprimir as alterações no valor de uma variável toda vez que ela for atualizada, deve-se utilizar o comando display. Isto é especialmente útil quando você deseja rastrear e imprimir o valor de uma variável em um loop:

display 

Definir pontos de controle

Os pontos de controle e os pontos de interrupção condicionais estão intimamente relacionados, pois ambos respondem às mudanças em um programa. Watchpoints são usados ​​para rastrear alterações nos dados do código. Por exemplo, você pode querer que o programa seja interrompido sempre que o valor de uma variável for alterado. Veja como fazer isso com o GDB:

watch 

Depuração específica de thread com GDB

O GDB permite realizar depuração específica de thread ao trabalhar com programas multithread. Para demonstração, trabalharemos com um programa C simples que usa quatro threads para imprimir mensagens com cada thread.

Para visualizar os threads gerados atualmente em seu programa, use o informações comando:

info threads

Para trabalhar com um thread específico, você pode selecioná-lo na lista usando seu número de índice. Por exemplo:

thread 2

Depois de selecionar o thread, você pode percorrer seu fluxo de execução usando o etapa, próximo, e terminar comandos conforme demonstrado acima.

Depuração remota com GDB

Você também pode depurar remotamente programas localizados em um sistema diferente. Para fazer isso, você precisa configurar o gdbserver na máquina de destino. Você pode instalá-lo facilmente usando o gerenciador de pacotes padrão da sua distribuição ou outros gerenciadores de pacotes que você instalou em seu sistema.

Por exemplo, para instalar o gdbserver em seus sistemas baseados em Ubuntu ou Debian, use APT:

sudo apt install gdbserver

Uma vez instalado, vá para a pasta do binário e execute este comando para iniciar o gdbserver:

gdbserver :

gdbserver deve retornar a saída que está ativa e escutando na porta que você definiu. Agora na máquina cliente, inicie o GDB e conecte-se ao servidor remoto usando o alvo comando:

target remote :

Escrevendo scripts GDB para automatizar a depuração

O GDB permite que os programadores escrevam scripts GDB que executarão comandos GDB automaticamente. Isso ajuda imensamente quando você tenta depurar a mesma parte de um código várias vezes. Em vez de ter que definir o ponto de interrupção, percorrer o código e imprimir valores de variáveis ​​​​cada vez que carregar o binário, você pode usar um script GDB para automatizar todo o processo.

Aqui está um exemplo:

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

No script acima, você está dizendo ao GDB para habilitar o log e salvar o log em um arquivo chamado amostra.out, em seguida, defina um ponto de interrupção no principal função.

Para o ponto de interrupção número 1, neste caso, o ponto de interrupção na função principal, execute os seguintes comandos: rastreamento de retorno, imprimir, continuar. Basicamente, o GDB irá primeiro executar um backtrace, depois imprimir o valor da variável "N", continuar a execução e, finalmente, encerrar.

Para executar este script, use:

gdb -x